diff --git a/.changeset/cuddly-spoons-itch.md b/.changeset/cuddly-spoons-itch.md new file mode 100644 index 0000000000..92858c73b3 --- /dev/null +++ b/.changeset/cuddly-spoons-itch.md @@ -0,0 +1,8 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +Introduced `proxyRequestLogger` to log proxy requests in the CLI. + +- Show the request URL and method in the console when a proxy request is made. +- Show proxy response status code diff --git a/.changeset/curvy-lies-laugh.md b/.changeset/curvy-lies-laugh.md new file mode 100644 index 0000000000..93258fd115 --- /dev/null +++ b/.changeset/curvy-lies-laugh.md @@ -0,0 +1,10 @@ +--- +'@equinor/fusion-framework-react-components-people-provider': patch +'@equinor/fusion-framework-cli': patch +--- + +Updating fusion-wc-person to fix issues when using selectedPerson = null in PersonSelect component. + +Updated the following dependencies + +- `@equinor/fusion-wc-person` from `^3.0.1` to `^3.0.3` in `packages/cli/package.json` and `packages/react/components/people-resolver/package.json`. diff --git a/.changeset/giant-points-fetch.md b/.changeset/giant-points-fetch.md new file mode 100644 index 0000000000..774cfea11e --- /dev/null +++ b/.changeset/giant-points-fetch.md @@ -0,0 +1,5 @@ +--- +'@equinor/fusion-framework-cli': patch +--- + +Generated base manifest from package will now include `StandardIncludeAssetExtensions` as `allowedExtensions` diff --git a/.changeset/good-wasps-exercise.md b/.changeset/good-wasps-exercise.md new file mode 100644 index 0000000000..238b101cf8 --- /dev/null +++ b/.changeset/good-wasps-exercise.md @@ -0,0 +1,5 @@ +--- +'@equinor/fusion-framework-docs': minor +--- + +Documenting the new CLI commands in vue-press. diff --git a/.changeset/hot-ears-fix.md b/.changeset/hot-ears-fix.md new file mode 100644 index 0000000000..c770b30bcb --- /dev/null +++ b/.changeset/hot-ears-fix.md @@ -0,0 +1,20 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +Create a plugin `externalPublicPlugin` to fix the issue with serving the `index.html` file from the specified external public directory. Vite mode `spa` will not serve the `index.html` file from the specified external public directory. + +- Enhanced the middleware to intercept requests and serve the `index.html` file from the specified external public directory. +- Transformed the HTML using Vite's `transformIndexHtml` method. +- Applied appropriate content headers and additional configured headers before sending the response. + +```typescript +const viteConfig = defineConfig({ + // vite configuration + root: './src', // this where vite will look for the index.html file + plugins: [ + // path which contains the index.html file + externalPublicPlugin('./my-portal'), + ], +}); +``` diff --git a/.changeset/nervous-cougars-love.md b/.changeset/nervous-cougars-love.md new file mode 100644 index 0000000000..2355a22fbb --- /dev/null +++ b/.changeset/nervous-cougars-love.md @@ -0,0 +1,19 @@ +--- +'@equinor/fusion-framework-cookbook-app-react-environment-variables': patch +'@equinor/fusion-framework-cookbook-app-react-bookmark-advanced': patch +'@equinor/fusion-framework-cookbook-app-react-feature-flag': patch +'@equinor/fusion-framework-cookbook-app-react-bookmark': patch +'@equinor/fusion-framework-cookbook-app-react-ag-grid': patch +'@equinor/fusion-framework-cookbook-app-react-context': patch +'@equinor/fusion-framework-cookbook-app-react-module': patch +'@equinor/fusion-framework-cookbook-app-react-people': patch +'@equinor/fusion-framework-cookbook-app-react-msal': patch +'@equinor/fusion-framework-cookbook-app-vanilla': patch +'@equinor/fusion-framework-cookbook-app-react': patch +'@equinor/fusion-framework-cookbook-app-react-context-custom-error': patch +'@equinor/fusion-framework-cookbook-app-react-router': patch +--- + +Cleaned up app config + +Removed `app.config.*` from the cookbook apps to prevent confusion when using the cookbook apps as a template for new apps. diff --git a/.changeset/pink-laws-cough.md b/.changeset/pink-laws-cough.md new file mode 100644 index 0000000000..28199f507d --- /dev/null +++ b/.changeset/pink-laws-cough.md @@ -0,0 +1,15 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +Updated commands in CLI to reflect purpose of the command: +- renamed `config` to `build-config` to generate build config of an application. +- renamed `pack`to `build-pack` to bundle an application. +- added `build-manifest` command to generate build manifest of an application. + +> [!WARNING] +> Config callback for `manifest` and `config` now allows `void` return type. +> Return value from callback is now merged with default config instead of replacing it, this might be a breaking change for some applications. + +> [!NOTE] +> This mean that `mergeAppConfig` and `mergeManifestConfig` functions are no longer needed and can be removed from the application. diff --git a/.changeset/quiet-scissors-breathe.md b/.changeset/quiet-scissors-breathe.md new file mode 100644 index 0000000000..c780be252f --- /dev/null +++ b/.changeset/quiet-scissors-breathe.md @@ -0,0 +1,64 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +The `appProxyPlugin` is a Vite plugin designed to proxy requests to a Fusion app backend. +It sets up proxy rules for API and bundle requests and serves the app configuration and manifest based on the app key and version. + +Key Features: + +1. Proxy Configuration: + + - Proxies API calls to the Fusion apps backend. + - Proxies bundle requests to the Fusion apps backend. + - Uses a base path `proxyPath` for proxying. + - Captures and reuses authorization tokens for asset requests. + +2. **App Configuration and Manifest**: + + - Serves the app configuration if the request matches the current app and version. + - Serves the app manifest if the request matches the current app. + +3. **Middleware Setup**: + - Sets up middleware to handle requests for app configuration, manifest, and local bundles. + +This plugin is used by the CLI for local development, but design as exportable for custom CLI to consume applications from other API`s + +example configuration: +```typescript +const viteConfig = defineConfig({ + // vite configuration + plugins: [ + appProxyPlugin({ + proxy: { + path: '/app-proxy', + target: 'https://fusion-s-apps-ci.azurewebsites.net/', + // optional callback when matched request is proxied + onProxyReq: (proxyReq, req, res) => { + proxyReq.on('response', (res) => { console.log(res.statusCode) }); + }, + }, + // optional, but required for serving local app configuration, manifest and resources + app: { + key: 'my-app', + version: '1.0.0', + generateConfig: async () => ({}), + generateManifest: async () => ({}), + }, + }), + ], +}); +``` + +example usage: +```typescript +// Example API calls +fetch('/app-proxy/apps/my-app/builds/1.0.0/config'); // local +fetch('/app-proxy/apps/my-app/builds/0.0.9/config'); // proxy +fetch('/app-proxy/apps/other-app/builds/1.0.0/config'); // proxy + +// Example asset calls +fetch('/app-proxy/bundles/my-app/builds/1.0.0/index.js'); // local +fetch('/app-proxy/bundles/my-app/builds/0.0.9/index.js'); // proxy +``` + diff --git a/.changeset/rare-carrots-give.md b/.changeset/rare-carrots-give.md new file mode 100644 index 0000000000..1f03263f5d --- /dev/null +++ b/.changeset/rare-carrots-give.md @@ -0,0 +1,78 @@ +--- +"@equinor/fusion-framework-cli": major +--- + +Adding new commands for app management, `build-publish`, `build-pack`, `build-upload`, `build-config`, `build-manifest` and `build-tag`. + +Introduces new parameters to the `build-config` command for publishing the app config to a build version. + +Commands: + +- `build-pack` - Bundle the app for distribution + - `-o, --output ` - Output directory for the packed app + - `-a, --archive` - Archive name for the packed app +- `build-upload` - Upload the packed app to the Fusion App Store + - `-b, --bundle ` - Path to the packed app bundle + - `-e, --env ` - Environment to upload the app to + - `-s, --service ` - Custom app service +- `build-tag` - Tag the uploaded app with a version + - `-t, --tag ` - Tag to apply to the uploaded app + - `-v, --version ` - Version to attach to the tag + - `-e, --env ` - Environment to tag the app in + - `-s, --service ` - Custom app service +- `build-publish` - Publish the app config to a build version + - `-t, --tag ` - Tag to apply to the uploaded app + - `-e, --env ` - Environment to tag the app in + - `-s, --service ` - Custom app service +- `build-config` - Publish the app config to a build version + - `-o, --output ` - Output file for the app config + - `-c, --config ` - Path to the app config file (for config generation) + - `-p, --publish` - Publish the app config to the build version + - `-e, --env ` - Environment to publish the app config to + - `-s, --service ` - Custom app service +- `build-manifest` - Creates the build manifest to publish with app + - `-o, --output ` - Output file for manifest + - `-c, --config ` - Manifest config file + +simple usage: +```sh +fusion-framework-cli app build-publish -e ci +``` + +complex usage: +```sh +fusion-framework-cli app build-pack -o ./dist -a my-app.zip +fusion-framework-cli app build-upload -b ./dist/my-app.zip -e ci +fusion-framework-cli app build-tag -t my-tag -v 1.0.0 -e ci +``` + +After publishing a build of an app, the app config should be uploaded to the build version. This is done by running the `build-config` command. + +```sh +# Publish the app config to the build version +fusion-framework-cli app build-config -p -e ci + +# Publish the app config to a specific build tag +fusion-framework-cli app build-config -p preview -e ci + +# Publish the app config to a specific build version +fusion-framework-cli app build-config -p 1.0.0 -e ci +``` + +__breaking changes:__ + +- renaming all commands accociated with build. +- The app-config endpoints is now an object containing url and scopes, where name is the object key: + + ```ts + environment: { + myProp: 'foobar', + }, + endpoints: { + api: { + url: 'https://foo.bars' + scopes: ['foobar./default'] + }, + }, + ``` +- The `config` command has been removed, use `build-config` instead diff --git a/.changeset/real-kiwis-flow.md b/.changeset/real-kiwis-flow.md new file mode 100644 index 0000000000..9e85b21f21 --- /dev/null +++ b/.changeset/real-kiwis-flow.md @@ -0,0 +1,5 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +when building an application the `AppAssetExportPlugin` is now added to the `ViteConfig` and configure to include `manifest.build.allowedExtensions` diff --git a/.changeset/slimy-buses-give.md b/.changeset/slimy-buses-give.md new file mode 100644 index 0000000000..3048179a16 --- /dev/null +++ b/.changeset/slimy-buses-give.md @@ -0,0 +1,65 @@ +--- +'@equinor/fusion-framework-module-app': major +'@equinor/fusion-framework-react-app': patch +'@equinor/fusion-framework-react-components-bookmark': patch +'@equinor/fusion-framework-react': minor +'@equinor/fusion-framework-legacy-interopt': patch +--- + +Adjusted module to the new app service API. + +> [!WARNING] +> This will introduce breaking changes to the configuration of `AppConfigurator.client`. + +**Added** + +- Introduced `AppClient` class to handle application manifest and configuration queries. +- Added `zod` to validate the application manifest. + +**Changed** + +- Updated `AppModuleProvider` to use `AppClient` for fetching application manifests and configurations. +- Modified `AppConfigurator` to utilize `AppClient` for client configuration. +- Updated `useApps` hook with new input parameter for `filterByCurrentUser` in `fusion-framework-react`. + +**Migration** + +before: + +```ts +configurator.setClient({ + getAppManifest: { + client: { + fn: ({ appKey }) => httpClient.json$(`/apps/${appKey}`), + }, + key: ({ appKey }) => appKey, + }, + getAppManifests: { + client: { + fn: () => httpClient.json$(`/apps`), + }, + key: () => `all-apps`, + }, + getAppConfig: { + client: { + fn: ({ appKey }) => httpClient.json$(`/apps/${appKey}/config`), + }, + key: ({ appKey }) => appKey, + }, +}); +``` + +after: + +```ts +import { AppClient } from `@equinor/fusion-framework-module-app`; +configurator.setClient(new AppClient()); +``` + +custom client implementation: + +```ts +import { AppClient } from `@equinor/fusion-framework-module-app`; +class CustomAppClient implements IAppClient { ... } +configurator.setClient(new CustomAppClient()); +``` diff --git a/.changeset/slow-apes-remember.md b/.changeset/slow-apes-remember.md new file mode 100644 index 0000000000..49ddffdb60 --- /dev/null +++ b/.changeset/slow-apes-remember.md @@ -0,0 +1,22 @@ +--- +'@equinor/fusion-framework-cli': minor +--- + +**App Assets Export Plugin** + +Create a plugin that exports assets from the app's source code. +This plugin resolves the issue where assets are not extracted from the app's source code since the app is in `lib` mode. + +```typescript +export default { + plugins: [ + AppAssetExportPlugin( + include: createExtensionFilterPattern( + manifest.build.allowedExtensions + ), + ), + ] +} +``` + +see readme for more information. diff --git a/cookbooks/app-react-ag-grid/app.config.js b/cookbooks/app-react-ag-grid/app.config.js deleted file mode 100644 index fa1c84c703..0000000000 --- a/cookbooks/app-react-ag-grid/app.config.js +++ /dev/null @@ -1,13 +0,0 @@ -// demo -export default () => ({ - // "appKey": "", - "manifest": { - "key": "ag-grid" - }, - "environment": {}, - "endpoints": { - "api": "https://foo.barz" - }, - // override portal lookup - "portalHost": undefined -}); diff --git a/cookbooks/app-react-assets/app.manifest.config.ts b/cookbooks/app-react-assets/app.manifest.config.ts new file mode 100644 index 0000000000..8c3815a8c4 --- /dev/null +++ b/cookbooks/app-react-assets/app.manifest.config.ts @@ -0,0 +1,7 @@ +import { defineAppManifest } from '@equinor/fusion-framework-cli'; + +export default defineAppManifest(async (env) => { + return { + appKey: 'test-assets', + }; +}); diff --git a/cookbooks/app-react-assets/package.json b/cookbooks/app-react-assets/package.json new file mode 100644 index 0000000000..d4aeb1b678 --- /dev/null +++ b/cookbooks/app-react-assets/package.json @@ -0,0 +1,24 @@ +{ + "name": "@equinor/fusion-framework-cookbook-app-react-assets", + "version": "0.0.1", + "description": "", + "private": true, + "type": "module", + "main": "src/index.ts", + "scripts": { + "build": "fusion-framework-cli app build", + "dev": "fusion-framework-cli app dev", + "docker": "cd .. && sh docker-script.sh app-react" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@equinor/fusion-framework-cli": "workspace:^", + "@equinor/fusion-framework-react-app": "workspace:^", + "@types/react": "^18.2.50", + "@types/react-dom": "^18.2.7", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "typescript": "^5.5.4" + } +} diff --git a/cookbooks/app-react-assets/src/App.tsx b/cookbooks/app-react-assets/src/App.tsx new file mode 100644 index 0000000000..b3d90d9a0d --- /dev/null +++ b/cookbooks/app-react-assets/src/App.tsx @@ -0,0 +1,9 @@ +import memeUrl from './mount_batur.jpg'; + +export const App = () => ( +
+ +
+); + +export default App; diff --git a/cookbooks/app-react-assets/src/config.ts b/cookbooks/app-react-assets/src/config.ts new file mode 100644 index 0000000000..3e381c7bfb --- /dev/null +++ b/cookbooks/app-react-assets/src/config.ts @@ -0,0 +1,18 @@ +import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app'; + +export const configure: AppModuleInitiator = (configurator, env) => { + /** print render environment arguments */ + console.log('configuring application', env); + + /** callback when configurations is created */ + configurator.onConfigured((config) => { + console.log('application config created', config); + }); + + /** callback when the application modules has initialized */ + configurator.onInitialized((instance) => { + console.log('application config initialized', instance); + }); +}; + +export default configure; diff --git a/cookbooks/app-react-assets/src/global.d.ts b/cookbooks/app-react-assets/src/global.d.ts new file mode 100644 index 0000000000..053f86f42b --- /dev/null +++ b/cookbooks/app-react-assets/src/global.d.ts @@ -0,0 +1,4 @@ +declare module '*.jpg' { + const value: string; + export default value; +} diff --git a/cookbooks/app-react-assets/src/index.ts b/cookbooks/app-react-assets/src/index.ts new file mode 100644 index 0000000000..7e07c40196 --- /dev/null +++ b/cookbooks/app-react-assets/src/index.ts @@ -0,0 +1,30 @@ +import { createElement } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { ComponentRenderArgs, makeComponent } from '@equinor/fusion-framework-react-app'; + +import configure from './config'; +import App from './App'; + +/** create a render component */ +const appComponent = createElement(App); + +/** create React render root component */ +const createApp = (args: ComponentRenderArgs) => makeComponent(appComponent, args, configure); + +/** Render function */ +export const renderApp = (el: HTMLElement, args: ComponentRenderArgs) => { + /** make render element */ + const app = createApp(args); + + /** create render root from provided element */ + const root = createRoot(el); + + /** render Application */ + root.render(createElement(app)); + + /** Teardown */ + return () => root.unmount(); +}; + +export default renderApp; diff --git a/cookbooks/app-react-assets/src/mount_batur.jpg b/cookbooks/app-react-assets/src/mount_batur.jpg new file mode 100644 index 0000000000..ce0b7c571f Binary files /dev/null and b/cookbooks/app-react-assets/src/mount_batur.jpg differ diff --git a/cookbooks/app-react-assets/tsconfig.json b/cookbooks/app-react-assets/tsconfig.json new file mode 100644 index 0000000000..73aff80db3 --- /dev/null +++ b/cookbooks/app-react-assets/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "rootDir": "src", + "jsx": "react-jsx", + }, + "references": [ + { + "path": "../../packages/react/app" + }, + { + "path": "../../packages/cli" + }, + ], + "include": [ + "src/**/*", + "src/global.d.ts" +], + "exclude": [ + "node_modules", + "lib" + ] +} \ No newline at end of file diff --git a/cookbooks/app-react-bookmark-advanced/app.config.js b/cookbooks/app-react-bookmark-advanced/app.config.js deleted file mode 100644 index aa75e4a4c8..0000000000 --- a/cookbooks/app-react-bookmark-advanced/app.config.js +++ /dev/null @@ -1,10 +0,0 @@ -// demo -export default () => ({ - // "appKey": "", - "environment": { - "scope": "foobar" - }, - "endpoints": { - "api": "https://foo.barz" - } -}); diff --git a/cookbooks/app-react-bookmark/app.config.js b/cookbooks/app-react-bookmark/app.config.js deleted file mode 100644 index 191aa57067..0000000000 --- a/cookbooks/app-react-bookmark/app.config.js +++ /dev/null @@ -1,13 +0,0 @@ -// demo -export default () => ({ - "manifest": { - "key": "fusion-bookmark", - "name":" Fusion Bookmark" - }, - "environment": { - "scope": "foobar" - }, - "endpoints": { - "api": "https://foo.barz" - } -}); diff --git a/cookbooks/app-react-context/app.manifest.config.ts b/cookbooks/app-react-context/app.manifest.config.ts deleted file mode 100644 index c1d1380a6c..0000000000 --- a/cookbooks/app-react-context/app.manifest.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((env, { base }) => { - if (env.command === 'serve') { - return mergeManifests(base, { - key: 'context', - }); - } - return base; -}); diff --git a/cookbooks/app-react-environment-variables/app.config.ts b/cookbooks/app-react-environment-variables/app.config.ts deleted file mode 100644 index ecfe248f12..0000000000 --- a/cookbooks/app-react-environment-variables/app.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -// demo -import { mergeAppConfigs, defineAppConfig } from '@equinor/fusion-framework-cli'; -export default defineAppConfig((_nev, { base }) => - mergeAppConfigs(base, { - environment: { - scope: 'foobar', - }, - endpoints: { - api: 'https://foo.bars', - }, - }), -); diff --git a/cookbooks/app-react-environment-variables/app.manifest.config.ts b/cookbooks/app-react-environment-variables/app.manifest.config.ts deleted file mode 100644 index b895b47650..0000000000 --- a/cookbooks/app-react-environment-variables/app.manifest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((env, { base }) => { - return mergeManifests(base, { - key: 'environment-variables', - }); -}); diff --git a/cookbooks/app-react-feature-flag/app.manifest.config.ts b/cookbooks/app-react-feature-flag/app.manifest.config.ts deleted file mode 100644 index 2ad22c46b4..0000000000 --- a/cookbooks/app-react-feature-flag/app.manifest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((_env, { base }) => { - return mergeManifests(base, { - key: 'feature-flag', - }); -}); diff --git a/cookbooks/app-react-module/app.config.js b/cookbooks/app-react-module/app.config.js deleted file mode 100644 index c06444030f..0000000000 --- a/cookbooks/app-react-module/app.config.js +++ /dev/null @@ -1,5 +0,0 @@ -export default () => ({ - "manifest": { - "key": "demo-module" - } -}); diff --git a/cookbooks/app-react-msal/app.manifest.config.ts b/cookbooks/app-react-msal/app.manifest.config.ts deleted file mode 100644 index f4a8af3069..0000000000 --- a/cookbooks/app-react-msal/app.manifest.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((env, { base }) => { - if (env.command === 'serve') { - return mergeManifests(base, { - key: 'msal', - }); - } - return base; -}); diff --git a/cookbooks/app-react-people/app.manifest.config.ts b/cookbooks/app-react-people/app.manifest.config.ts deleted file mode 100644 index 70f2b4d314..0000000000 --- a/cookbooks/app-react-people/app.manifest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((_env, { base }) => { - return mergeManifests(base, { - key: 'cookbook-people', - name: 'cookbook-people', - }); -}); diff --git a/cookbooks/app-react/README.md b/cookbooks/app-react/README.md new file mode 100644 index 0000000000..5874b84d21 --- /dev/null +++ b/cookbooks/app-react/README.md @@ -0,0 +1,3 @@ +# React cookbook + +App for cooking with Fusion-Framework and React diff --git a/cookbooks/app-react/app.config.ts b/cookbooks/app-react/app.config.ts index ecfe248f12..84d401a4fb 100644 --- a/cookbooks/app-react/app.config.ts +++ b/cookbooks/app-react/app.config.ts @@ -1,12 +1,11 @@ -// demo -import { mergeAppConfigs, defineAppConfig } from '@equinor/fusion-framework-cli'; -export default defineAppConfig((_nev, { base }) => - mergeAppConfigs(base, { - environment: { - scope: 'foobar', - }, +import { defineAppConfig } from '@equinor/fusion-framework-cli'; + +export default defineAppConfig((_nev) => { + return { endpoints: { - api: 'https://foo.bars', + api: { + url: 'https://foo.bars', + }, }, - }), -); + }; +}); diff --git a/cookbooks/app-react/app.manifest.config.ts b/cookbooks/app-react/app.manifest.config.ts new file mode 100644 index 0000000000..a93793319d --- /dev/null +++ b/cookbooks/app-react/app.manifest.config.ts @@ -0,0 +1,3 @@ +import { defineAppManifest } from '@equinor/fusion-framework-cli'; + +export default defineAppManifest(async (_env) => {}); diff --git a/cookbooks/app-react/app.manifest.ts b/cookbooks/app-react/app.manifest.ts deleted file mode 100644 index d08bf9383e..0000000000 --- a/cookbooks/app-react/app.manifest.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((env, { base }) => { - if (env.command === 'serve') { - return mergeManifests(base, { - key: 'simple', - }); - } - return base; -}); diff --git a/cookbooks/app-react/test.json b/cookbooks/app-react/test.json deleted file mode 100644 index e44b24e126..0000000000 --- a/cookbooks/app-react/test.json +++ /dev/null @@ -1 +0,0 @@ -{"key":"fusion-framework-cookbook-app-react","entry":"src/index.ts","version":"4.0.23","name":"fusion-framework-cookbook-app-react","shortName":"fusion-framework-cookbook-app-react","description":""} \ No newline at end of file diff --git a/cookbooks/app-vanilla/app.manifest.config.ts b/cookbooks/app-vanilla/app.manifest.config.ts deleted file mode 100644 index 114fd436b7..0000000000 --- a/cookbooks/app-vanilla/app.manifest.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; - -export default defineAppManifest((_env, { base }) => - mergeManifests(base, { - key: 'vanilla', - }), -); diff --git a/cookbooks/poc-portal/.eslintrc b/cookbooks/poc-portal/.eslintrc new file mode 100644 index 0000000000..64d514154c --- /dev/null +++ b/cookbooks/poc-portal/.eslintrc @@ -0,0 +1,12 @@ +{ + "extends": "@equinor/eslint-config-fusion-react", + "plugins": ["rxjs"], + "rules": { + "@typescript-eslint/no-empty-interface": [ + "warn", + { + "allowSingleExtends": true + } + ] + } +} \ No newline at end of file diff --git a/cookbooks/poc-portal/.gitignore b/cookbooks/poc-portal/.gitignore new file mode 100644 index 0000000000..141691465e --- /dev/null +++ b/cookbooks/poc-portal/.gitignore @@ -0,0 +1 @@ +/dev-portal \ No newline at end of file diff --git a/cookbooks/poc-portal/README.md b/cookbooks/poc-portal/README.md new file mode 100644 index 0000000000..15fbc2dc09 --- /dev/null +++ b/cookbooks/poc-portal/README.md @@ -0,0 +1,5 @@ +> [!NOTE] +> This application is a proof of concept for a portal as an application from a static hosted site. + +> [!WARNING] +> This is __NOT__ an example of a production ready application. This is a proof of concept and should __NOT__ be used as a reference for production applications. \ No newline at end of file diff --git a/cookbooks/poc-portal/app.config.ts b/cookbooks/poc-portal/app.config.ts new file mode 100644 index 0000000000..eeb1b437a3 --- /dev/null +++ b/cookbooks/poc-portal/app.config.ts @@ -0,0 +1,12 @@ +import { defineAppConfig } from '@equinor/fusion-framework-cli'; + +export default defineAppConfig(() => ({ + environment: { + apps: [ + { + context: 'something', + key: 'my-app', + }, + ], + }, +})); diff --git a/cookbooks/poc-portal/package.json b/cookbooks/poc-portal/package.json new file mode 100644 index 0000000000..1f14b98010 --- /dev/null +++ b/cookbooks/poc-portal/package.json @@ -0,0 +1,35 @@ +{ + "name": "poc-portal", + "version": "1.0.0", + "description": "", + "main": "src/index.tsx", + "private": true, + "scripts": { + "dev": "fusion-framework-cli app dev -d './dev-portal'", + "build:portal": "vite build -c vite.config.server.ts" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "@equinor/fusion-framework": "workspace:*", + "@equinor/fusion-framework-cli": "workspace:*", + "@equinor/fusion-framework-module": "workspace:*", + "@equinor/fusion-framework-module-app": "workspace:*", + "@equinor/fusion-framework-module-http": "workspace:*", + "@equinor/fusion-framework-module-msal": "workspace:*", + "@equinor/fusion-framework-module-service-discovery": "workspace:*", + "@equinor/fusion-framework-react": "workspace:*", + "@equinor/fusion-framework-react-app": "workspace:*", + "@vitejs/plugin-react": "^4.3.1", + "is-mergeable-object": "^1.1.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "vite": "^5.3.4", + "vite-plugin-environment": "^1.1.3" + }, + "devDependencies": { + "@types/react": "^18.2.50", + "@types/react-dom": "^18.3.0" + } +} diff --git a/cookbooks/poc-portal/src/AppList.tsx b/cookbooks/poc-portal/src/AppList.tsx new file mode 100644 index 0000000000..2b5d5f0db2 --- /dev/null +++ b/cookbooks/poc-portal/src/AppList.tsx @@ -0,0 +1,8 @@ +import { useApps } from '@equinor/fusion-framework-react/app'; + +export const AppList = () => { + const { apps } = useApps(); + return
    {apps?.map((app) =>
  • {app.name}
  • )}
; +}; + +export default AppList; diff --git a/cookbooks/poc-portal/src/Framework.tsx b/cookbooks/poc-portal/src/Framework.tsx new file mode 100644 index 0000000000..2833b9c20f --- /dev/null +++ b/cookbooks/poc-portal/src/Framework.tsx @@ -0,0 +1,40 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { Suspense } from 'react'; +import { ModulesInstanceType } from '@equinor/fusion-framework-module'; +import { createFrameworkProvider } from '@equinor/fusion-framework-react'; + +import { type AppModule, enableAppModule } from '@equinor/fusion-framework-module-app'; +import AppList from './AppList'; + +export const Framework = (args: { modules: ModulesInstanceType<[]> }) => { + const FrameworkProvider = createFrameworkProvider((configurator) => { + enableAppModule(configurator, (builder) => { + builder.setAssetUri('/apps-proxy/bundles/apps'); + }); + + if (process.env.NODE_ENV === 'development') { + configurator.configureHttpClient('app', { + baseUri: new URL('/apps-proxy/', window.location.href).href, + defaultScopes: ['5a842df8-3238-415d-b168-9f16a6a6031b/.default'], + }); + } + + configurator.onInitialized((instance) => { + (instance as unknown as ModulesInstanceType<[AppModule]>).app.setCurrentApp( + // 'fusion-framework-cookbook-app-react', + 'poc-portal', + ); + // @ts-ignore + window.test = instance; + }); + }, args.modules); + return ( + :/

}> + + + +
+ ); +}; + +export default Framework; diff --git a/cookbooks/poc-portal/src/config.ts b/cookbooks/poc-portal/src/config.ts new file mode 100644 index 0000000000..3e381c7bfb --- /dev/null +++ b/cookbooks/poc-portal/src/config.ts @@ -0,0 +1,18 @@ +import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app'; + +export const configure: AppModuleInitiator = (configurator, env) => { + /** print render environment arguments */ + console.log('configuring application', env); + + /** callback when configurations is created */ + configurator.onConfigured((config) => { + console.log('application config created', config); + }); + + /** callback when the application modules has initialized */ + configurator.onInitialized((instance) => { + console.log('application config initialized', instance); + }); +}; + +export default configure; diff --git a/cookbooks/poc-portal/src/index.tsx b/cookbooks/poc-portal/src/index.tsx new file mode 100644 index 0000000000..8740d58bd0 --- /dev/null +++ b/cookbooks/poc-portal/src/index.tsx @@ -0,0 +1,11 @@ +import { createElement } from 'react'; +import { createRoot } from 'react-dom/client'; + +import { ModulesInstanceType } from '@equinor/fusion-framework-module'; +import { Framework } from './Framework'; + +export const render = (el: HTMLElement, modules: ModulesInstanceType<[]>) => { + createRoot(el).render(createElement(Framework, { modules })); +}; + +export default render; diff --git a/cookbooks/poc-portal/src/portal/bootloader.ts b/cookbooks/poc-portal/src/portal/bootloader.ts new file mode 100644 index 0000000000..82f3cb6cd4 --- /dev/null +++ b/cookbooks/poc-portal/src/portal/bootloader.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/ban-ts-comment */ +import { ModulesConfigurator } from '@equinor/fusion-framework-module'; + +import { configureHttpClient } from '@equinor/fusion-framework-module-http'; +import { configureMsal } from '@equinor/fusion-framework-module-msal'; +import { enableServiceDiscovery } from '@equinor/fusion-framework-module-service-discovery'; + +const environment = 'ci'; + +const configurator = new ModulesConfigurator(); + +const importWithoutVite = (path: string) => import(/* @vite-ignore */ path); + +configurator.logger.level = process.env.NODE_ENV === 'development' ? 4 : 1; + +configurator.addConfig( + configureHttpClient('service_discovery', { + baseUri: `https://discovery.fusion.equinor.com`, + defaultScopes: ['5a842df8-3238-415d-b168-9f16a6a6031b/.default'], + }), +); + +enableServiceDiscovery(configurator, async (builder) => { + builder.configureServiceDiscoveryClientByClientKey( + 'service_discovery', + `/service-registry/environments/${environment}/services`, + ); +}); + +configurator.addConfig( + configureMsal( + { + tenantId: '3aa4a235-b6e2-48d5-9195-7fcf05b459b0', + clientId: '9b707e3a-3e90-41ed-a47e-652a1e3b53d0', + redirectUri: '/authentication/login-callback', + }, + { requiresAuth: true }, + ), +); + +(async () => { + const el = document.getElementById('app'); + const ref = await configurator.initialize(); + const { render } = await importWithoutVite('../src/index.tsx'); + // @ts-ignore + render(el, ref); +})(); diff --git a/cookbooks/poc-portal/src/portal/index.html b/cookbooks/poc-portal/src/portal/index.html new file mode 100644 index 0000000000..3e914f6d52 --- /dev/null +++ b/cookbooks/poc-portal/src/portal/index.html @@ -0,0 +1,16 @@ + + + + + + + Test portals + + + + +

Test portalss

+
🙀
+ + + \ No newline at end of file diff --git a/cookbooks/poc-portal/tsconfig.json b/cookbooks/poc-portal/tsconfig.json new file mode 100644 index 0000000000..e8a7b0ceb7 --- /dev/null +++ b/cookbooks/poc-portal/tsconfig.json @@ -0,0 +1,45 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "target": "ES6", + "declarationDir": "./dist/types", + "baseUrl": ".", + "jsx": "react-jsx" + }, + "references": [ + { + "path": "../../packages/modules/module" + }, + { + "path": "../../packages/modules/msal" + }, + { + "path": "../../packages/modules/http" + }, + { + "path": "../../packages/modules/app" + }, + { + "path": "../../packages/modules/service-discovery" + }, + { + "path": "../../packages/framework" + }, + { + "path": "../../packages/react/framework" + }, + { + "path": "../../packages/cli" + }, + + ], + "include": [ + "src/**/*", + "types/*" +, "app.config.ts" ], + "exclude": [ + "node_modules", + ], +} \ No newline at end of file diff --git a/cookbooks/poc-portal/vite.config.server.ts b/cookbooks/poc-portal/vite.config.server.ts new file mode 100644 index 0000000000..274f982a60 --- /dev/null +++ b/cookbooks/poc-portal/vite.config.server.ts @@ -0,0 +1,26 @@ +import { defineConfig } from 'vite'; +import { fileURLToPath } from 'url'; +import viteEnv from 'vite-plugin-environment'; // Import the 'viteEnv' function from the appropriate module + +import dns from 'dns'; +dns.setDefaultResultOrder('verbatim'); + +export default defineConfig({ + plugins: [ + viteEnv({ + NODE_ENV: process.env.mode, + FUSION_LOG_LEVEL: + (process.env.FUSION_LOG_LEVEL ?? process.env.mode === 'development') ? '4' : '1', + }), + ], + root: fileURLToPath(new URL('./src/portal', import.meta.url)), + build: { + outDir: fileURLToPath(new URL('./dev-portal', import.meta.url)), + emptyOutDir: true, + minify: false, + terserOptions: { + compress: false, + mangle: false, + }, + }, +}); diff --git a/packages/app/src/configure-modules.ts b/packages/app/src/configure-modules.ts index e4137e456d..86521a035a 100644 --- a/packages/app/src/configure-modules.ts +++ b/packages/app/src/configure-modules.ts @@ -50,9 +50,9 @@ export const configureModules = // @eikeland // TODO - remove check after fusion-cli is updated (app module is not enabled in fusion-cli) - if (args.env.manifest?.key) { + if (args.env.manifest?.appKey) { modules.event.dispatchEvent('onAppModulesLoaded', { - detail: { appKey: args.env.manifest.key, modules }, + detail: { appKey: args.env.manifest.appKey, modules }, }); } return modules; diff --git a/packages/cli/.eslintrc b/packages/cli/.eslintrc index 64d514154c..187f369a52 100644 --- a/packages/cli/.eslintrc +++ b/packages/cli/.eslintrc @@ -1,6 +1,5 @@ { "extends": "@equinor/eslint-config-fusion-react", - "plugins": ["rxjs"], "rules": { "@typescript-eslint/no-empty-interface": [ "warn", diff --git a/packages/cli/README.md b/packages/cli/README.md index dfb149c5d8..b72301376c 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -1,20 +1,42 @@ --- -title: usion Framework CLI +title: Fusion Framework CLI --- -Node CLI utility for working with Fusion Applications +### fusion-framework-cli -### [App](docs/app.md) +The cli, named `fusion-framework-cli`, contains tooling for running, building and publishing your application. -see [Configuration](docs/app-configuration.md) +The fusion-famework-cli should be installed as a devDependency. -- tooling for [Vitest](https://vitest.dev/) - 🚀 coming soon -- tooling for proxy configuration - 🚀 coming soon +```sh +pnpm add -D @equinor/fusion-framework-cli +``` -### Widget +You can always use the option `-h or --help or help` to get help for the CLI and its commands. + +Example: + +```sh +fusion-framework-cli help +``` + +### App Commands + +See [App Commands](/cli/docs/commands.md) + +### Configuration + +See [Configuration](/cli/docs/configuration.md) + +### Tooling + +- tooling for [Vitest](https://vitest.dev/) - 🚀 coming soon +- tooling for proxy configuration - 🚀 coming soon + +### Widget Tooling for developing widgets - 🚀 coming soon -### Portal +### Portal -Tooling for developing portals - 🚀 coming \ No newline at end of file +Tooling for developing portals - 🚀 coming diff --git a/packages/cli/TODO.md b/packages/cli/TODO.md new file mode 100644 index 0000000000..d3cafe3f5d --- /dev/null +++ b/packages/cli/TODO.md @@ -0,0 +1 @@ +- add howto set app entrypoint! \ No newline at end of file diff --git a/packages/cli/docs/api-authentication.md b/packages/cli/docs/api-authentication.md new file mode 100644 index 0000000000..6a3f2b5c24 --- /dev/null +++ b/packages/cli/docs/api-authentication.md @@ -0,0 +1,93 @@ +--- +title: Fusion Framework CLI - Commands +--- + +## Authentication + +To run any app commands involving the [app api](https://fusion-s-apps-ci.azurewebsites.net/swagger/index.html), you need to provide a valid fusion access_token. + +The CLI will look for the env varable `FUSION_TOKEN`. + +You can get an access token by using the azure `az` command in your local dev environment or in your github workflow. + +```sh +az login --scope "example-env-scope/.default" +az account get-access-token +``` + +See the [section](#authentication-in-github-actions) for using the framework-cli in a workflow with azure authentication. + +--- + +### Authentication in GitHub actions + +To run `fusion-framework-cli` commands towards the apps service, you need to set the env variable `FUSION_TOKEN` with a valid azure access_token in the the Equinor tenant. + +To get azure access_token in a action you need to: + +- Create azure service principal credentials to authenticate action and save them as secrets in your repository. +- use the action `azure/login@v2` with the secret credentials to authenticate. +- use the action `azure/cli@v2` to retreive access token for logged in user. +- Save access token as `FUSION_TOKEN` env variable. +- Run `fusion-framework-cli app XX` command like publish, upload or tag. + +Create a GitHub Action **secret** AZURE_CREDENTIALS with the value as an object like described in the example below: + +```json +{ + "clientSecret": "******", + "subscriptionId": "******", + "tenantId": "******", + "clientId": "******" +} +``` + +Define workflow steps that runs the `az` command to get a token and save the token in env variable ``FUSION_TOKEN``. +Handle versioning with changeset or please-release to create a semver of you application. + +```yaml +name: Publish to CI +description: Deploy application to CI env with fusion apps-service. + +inputs: + fusionEnv: ci + tag: latest + +on: + push: + branches: + - main + +jobs: + publish: + name: Publish to apps-service + runs-on: ubuntu-latest + steps: + - name: Azure login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Azure CLI + uses: azure/cli@v2 + with: + azcliversion: latest + inlineScript: | + export FUSION_TOKEN="$(az account get-access-token --scope example-env-scope\.default)" + + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4' + + - name: install dependencies + ... + + - name: Publish current version to apps-service + run: fusion-framework-cli app publish --tag ${{inputs.tag}} --env ${{inputs.fusionEnv}} + + - name: Upload config for this version and env + run: fusion-framework-cli app config --config app.config-ci.ts --publish ${{inputs.tag}} --env ${{inputs.fusionEnv}} + +``` + +You can also use package script to deploy to apps-service diff --git a/packages/cli/docs/app-configuration.md b/packages/cli/docs/app-configuration.md deleted file mode 100644 index 3030136091..0000000000 --- a/packages/cli/docs/app-configuration.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Fusion Framework CLI - App Configuration ---- - -## Config - - - -> this is the configuration served to the application from the application service - -the cli will look for a `app.config.{ts,js,json}` which will be provided to the configuration step of the application - -```ts -export type AppConfig = { - /** application config */ - environment?: Record; -}; -``` - -[see how to generate config](./app.md#config) - - -## Manifest - - - -the cli will look for a `app.manifest.config.{ts,js,json}` which will be provided to the configuration step as manifest - -> [!NOTE] -> by default the the application will generate a manifest based on the package.json - -[see how to generate config](./app.md#manifest) - -#### Resources - - - -When uploading resources used within the application, the need to be defined in the manifest. - -> [!NOTE] -> A new application service is under construction, which supports dynamic resources. -> Since resources will be deprecated in the future, so the CLI will not auto generate it from the `package.json` attribute - -__app.manifest.config.ts__ -```ts -import { defineAppManifest } from '@equinor/fusion-framework-cli'; -export default defineAppManifest((env, { base }) => { - return { - ...base, - resources: [ - 'static/foo.png', - 'static/bar.svg', - ], - }; -}); -``` - -By default [Vite](https://vitejs.dev/config/shared-options.html#publicdir) will copy the `public` folder, simply move the resources to the public folder - -for more advance, override the application vite config in `app.vite.config.ts` -```ts -/** @type {import('vite').UserConfig} */ -export default { - publicDir: 'resources' -} -``` - - -> [!IMPORTANT] -> Resources will be removed in future version of Fusion, the will come an migration guide of how to include static content - -## Vite - -the cli will look for a `app.vite.config.{ts,js,json}` [Vite Configuration](https://vitejs.dev/config/) - -> [!IMPORTANT] -> in most cases developers do not need to alter the base vite configuration, __this is only a option as a last resort__. - -> [!CAUTION] -> adding a `vite.config` to the base of your project will override Fusion Framework CLI base config, __NOT RECOMMENDED__ - diff --git a/packages/cli/docs/app.md b/packages/cli/docs/app.md deleted file mode 100644 index 742ab3417c..0000000000 --- a/packages/cli/docs/app.md +++ /dev/null @@ -1,63 +0,0 @@ ---- -title: Fusion Framework CLI - App ---- - -Application utilities - -## Commands - -```sh -fusion-framework-cli app --help -``` - -### dev - -develop an application (with [Vite](https://vitejs.dev/) and generic [Fusion](https://fusion.equinor.com/) portal) - -```sh -fusion-framework-cli app dev --help -``` - -### build -builds application -> only source files are included, the dev-port is __not__ bundled. - -```sh -fusion-framework-cli app build --help -``` - -### manifest -generates manifest -```sh -fusion-framework-cli app manifest --help -``` - -### config -generates application config -```sh -fusion-framework-cli app config --help -``` - -__example__ -```sh -fusion-framework-cli app config -o my-app.config.json -fdev portal config -e ci -k my-app --config-file my-app.config.json set -``` - - -### pack - -bundle application, generate manifest and zip output - -> [!NOTE] -> This function will be deprecated in the future, since new application service will supports `tar` - -```sh -fusion-framework-cli app pack --help -``` - -__example__ -```sh -fusion-framework-cli app pack -fdev portal upload -e ci -k my-app dist/app-bundle.zip -``` \ No newline at end of file diff --git a/packages/cli/docs/commands.md b/packages/cli/docs/commands.md new file mode 100644 index 0000000000..8e1aef0360 --- /dev/null +++ b/packages/cli/docs/commands.md @@ -0,0 +1,269 @@ +--- +title: Fusion Framework CLI - Commands +--- + +## Commands + +### fusion-framework-cli app + +The `app` command give you the possibility to manage your app from the commandline. There are commands for running, building, packaging and publishing your application. + +This should be run from your apps workspace root. + +``fusion-framework-cli app`` + +Commands: + +- [dev](#fusion-framework-cli-app-dev) - Start development server for application +- [build](#fusion-framework-cli-app-build) - Builds application +- [build-config](#fusion-framework-cli-app-build-config) - Generate config +- [manifest](#fusion-framework-cli-app-manifest) - Generate AppManifest for application +- [build-manifest](#fusion-framework-cli-app-build-manifest) - Generate build manifest for application +- [build-pack](#fusion-framework-cli-app-build-pack) - Create distributable app bundle of the application +- [build-publish](#fusion-framework-cli-app-build-publish) - Publish application to app api +- [build-upload](#fusion-framework-cli-app-build-upload) - Upload packaged app bundle to app api +- [build-tag](#fusion-framework-cli-app-build-tag) - Tag a published version +- help - display help for any command + +Options: + + | Option | Description | + |----------|-----------------------| + | -h, --help | display help for command | + +Example: + +```sh +fusion-framework-cli app +``` + +--- + +### fusion-framework-cli app dev + +The `dev` command starts a development server for current application. + +``fusion-framework-cli app dev`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -p, --port \ | dev-server port | + | -P, --portal \ | fusion portal host | + | -c, --config \ | use specified application config, by default search for app.config.{ts,js,json} | + | --manifest \ | use specified manifest, by default search for app.manifest.config.{ts,js,json} | + | --vite \ | use specified Vite config file, by default search for app.vite.config.{ts,js,json} | + | -F, --framework \ | application framework to build the application on, supported: [react] (default: "react") | + | -d, --dev-portal \ | Location of dev-portal you want to use | + | -h, --help | display help for command | + + +Example: + +```sh +fusion-framework-cli app dev -p 3001 +``` + +--- + +### fusion-framework-cli app build + +The `build` command compiles the application with vite. + +``fusion-framework-cli app build`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -o, --outDir, \ | output directory of package (default: "dist") | + | -c, --config \ | Use specified config file, see [here](https://vitejs.dev/guide/cli.html#build) | + | --vite \ | use specified Vite config file, by default search for app.config.vite.{ts,js,json} | + | -F, --framework \ | application framework to build the application on, supported: [react] (default: "react") | + | -h, --help | display help for command | + +Example: + +```sh +fusion-framework-cli app build -o dist +``` + +--- + +### fusion-framework-cli app build-config + +The `app build-config` command generates config file for the application. + +``fusion-framework-cli app build-config`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -o, --output \ | output file | + | -c, --config \ | application config file | + | -p, --publish \ | Publish app config to version [(semver \| current)] | + | -e, --env, \ | Fusion environment to build api urls from. used when publishing config. | + | -s, --service, \ | Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands. used when publishing config | + | -h, --help | display help for command | + +Example to publish config to version "1.0.3" in the "ci" environment: + +```sh +fusion-framework-cli app build-config -p 1.0.3 -e ci +``` + +--- + +### fusion-framework-cli app manifest + +The `app manifest` command generates AppManifest for the application. + +``fusion-framework-cli app manifest`` + +Options: + + | Option | Description | + |---------------------|------------------------| + | -o, --output \ | output file | + | -c, --config \ | manifest config file | + | -h, --help | display help for command | + +Example: + +```sh +fusion-framework-cli app build-manifest -o manifest.json +``` + +--- + +### fusion-framework-cli app build-manifest + +The `app build-manifest` command generates manifest file for the application. + +``fusion-framework-cli app build-manifest`` + +Options: + + | Option | Description | + |---------------------|------------------------| + | -o, --output \ | output file | + | -c, --config \ | manifest config file | + | -h, --help | display help for command | + +Example: + +```sh +fusion-framework-cli app build-manifest -o manifest.json +``` + +--- + +### fusion-framework-cli app build-pack + +The `app build-pack` command creates a distributable app bundle of the application. + +``fusion-framework-cli app build-pack`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -o, --outDir, \ | output directory of package (default: "dist") | + | -a, --archive, \ | output filename (default: "app-bundle.zip") | + | -h, --help | display help for command | + +Example: + +```sh +fusion-framework-cli app build-pack +``` + +--- + +### fusion-framework-cli app build-publish + +The `app build-publish` command is a convenience command to handle packing, publishing and tagging in one command. + +It runs the following commands in order: + +- `app build-pack` command that generates a zip file, +- `app build-upload` command that uploads the generated zip +- `app build-tag` command to tag the uploaded build. + +``fusion-framework-cli app build-publish`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -t, --tag, \ | Tagname to publish this build as (default: "latest") | + | -e, --env, \ | Fusion environment to build api urls from | + | -s, --service, \ | Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands | + | -h, --help | display help for command | + +Example that publishes a build and tag it with preview in ci: + +```sh +fusion-framework-cli app build-publish -t preview -e ci +``` + +--- + +### fusion-framework-cli app build-upload + +The `app build-upload` command takes a generated bundle file and uploads it to the appKey you are running the command from. the bundle must be a zip file, but support for tar.gz will come soon. + +``fusion-framework-cli app build-upload`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -b, --bundle, \ | The packaged app bundle file to upload (default: "app-bundle.zip") | + | -e, --env, \ | Fusion environment to build api urls from | + | -s, --service, \ | Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands | + | -h, --help | display help for command | + +Example that uploads zip package to ci: + +```sh +fusion-framework-cli app build-upload -b app-bundle.zip -e ci +``` + +--- + +### fusion-framework-cli app build-tag + +The `app build-tag` commands tags the specified version with preview or latest, where latest is default. The `--version` parameter must be a previously published build. + +``fusion-framework-cli app build-tag`` + +Options: + + | Option | Description | + |-------------------------|--------------------------------------------------------| + | -t, --tag, \ | Tag the published version with tagname [(latest \| preview)] (default: "latest") | + | -v, --version, \ | Version number to tag, must be a published version number | + | -e, --env, \ | Fusion environment to build api urls from | + | -s, --service, \ | Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands | + | -h, --help | display help for command | + +Example that tags version 1.0.3 with preview in ci: + +```sh +fusion-framework-cli app build-tag -v 1.0.3 -t preview -e ci +``` + +--- + +### Authentication + +To run any app commands involving the [app api](https://fusion-s-apps-ci.azurewebsites.net/swagger/index.html), you need to provide a valid fusion access_token. + +The CLI will look for the env varable `FUSION_TOKEN`. + +See the [section](/fusion-framework/cli/docs/api-authentication.html) for using the framework-cli in a workflow with azure authentication. + +--- diff --git a/packages/cli/docs/configuration.md b/packages/cli/docs/configuration.md new file mode 100644 index 0000000000..cd43ab01a2 --- /dev/null +++ b/packages/cli/docs/configuration.md @@ -0,0 +1,122 @@ +--- +title: Fusion Framework CLI - App Configuration +--- + +## Config + + + +> this is the configuration served to the application from the application service + +the cli will look for a `app.config.{ts,js,json}` which will be provided to the configuration step of the application + +## Config (optional) + +```ts +// app.config.ts +import { mergeAppConfigs, defineAppConfig } from '@equinor/fusion-framework-cli'; +export default defineAppConfig((_env, { base }) => + mergeAppConfigs(base, { + environment: { + fish: 'they can fly?', + shrimp: { + type: 'crustation', + desc: 'cockroach of the sea' + } + }, + endpoints: { + api: { + url: 'https://foo.bar', + scopes: ['default'], + }, + }, + }), +); +``` + +You can configure framework services in the `src/config.ts` file. + +> this is the configuration used by the application to configure framework services and modules + +```ts +// src/config.ts +import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app'; +export const configure: AppModuleInitiator = (configurator, { env }) => { + const { endpoints } = env.config.environment; + configurator.configureClient( 'api', endpoints.api ); +}; +``` + +### Manifest (optional) + +By default the CLI will create a manifest on best effort from `package.json` + +You can override defaults with mergeManifests + +```ts +// app.manifest.config.ts should be of type AppManifestExport +import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; + +export default defineAppManifest((env, { base }) => { + return mergeManifests( + base, + { + entryPoint: 'index.js' + } + ) +}); +``` + +#### Resources + + + +If the archive contains a file that isn't whitelisted by default it's possible to allow it by adding it's extension to the allowedExtensions array. + +**Default allowed extensions**: + +- .md +- .js +- .json +- .map + +> [!IMPORTANT] +> Resources are now deprecated, so the CLI will no longer auto generate it from the `package.json` attribute + +##### app.manifest.config.ts + +```ts +import { defineAppManifest } from '@equinor/fusion-framework-cli'; +export default defineAppManifest((env, { base }) => { + return { + ...base, + allowedExtensions: [ + '.png', + '.svg', + ], + }; +}); +``` + +By default [Vite](https://vitejs.dev/config/shared-options.html#publicdir) will copy the `public` folder, simply move the resources to the public folder + +for more advance, override the application vite config in `app.vite.config.ts` + +```ts +/** @type {import('vite').UserConfig} */ +export default { + publicDir: 'resources' +} +``` + +## Vite + +the cli will look for a `app.vite.config.{ts,js,json}` [Vite Configuration](https://vitejs.dev/config/) + +> [!IMPORTANT] +> in most cases developers do not need to alter the base vite configuration, **this is only a option as a last resort**. + +vite.config + +> [!CAUTION] +> adding a `vite.config` to the base of your project will override Fusion Framework CLI base config, **NOT RECOMMENDED** diff --git a/packages/cli/package.json b/packages/cli/package.json index 13215bf5b6..60d68df68a 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -21,37 +21,39 @@ ".": { "types": "./dist/types/lib/index.d.ts", "import": "./dist/lib/index.js" + }, + "./plugin-app-assets":{ + "types": "./dist/types/bin/plugins/app-assets/index.d.ts", + "import": "./dist/lib/plugins/app-assets/index.js" + }, + "./plugin-app-proxy": { + "types": "./dist/types/lib/plugins/app-proxy/index.d.ts", + "import": "./dist/lib/plugins/app-proxy/index.js" + }, + "./plugin-external-public": { + "types": "./dist/types/lib/plugins/external-public/index.d.ts", + "import": "./dist/lib/plugins/external-public/index.js" } }, "scripts": { "prebuild": "pnpm build:source", "build": "pnpm build:source && pnpm build:dev-server", + "build:clean": "rm -rf dist && rm -f tsconfig.tsbuildinfo && pnpm build", "build:source": "tsc -b", "build:dev-server": "vite build", "prepack": "pnpm build" }, "dependencies": { - "@equinor/eds-core-react": "^0.42.0", - "@equinor/eds-icons": "^0.21.0", - "@equinor/eds-tokens": "^0.9.2", - "@equinor/fusion-framework-app": "workspace:^", - "@equinor/fusion-framework-module-feature-flag": "workspace:^", - "@equinor/fusion-framework-react-components-people-provider": "workspace:^", - "@equinor/fusion-observable": "workspace:^", - "@equinor/fusion-wc-chip": "^1.2.1", - "@equinor/fusion-wc-person": "^3.0.3", - "@types/adm-zip": "^0.5.0", - "@types/semver": "^7.5.0", "@vitejs/plugin-react": "^4.0.4", "adm-zip": "^0.5.10", "chalk": "^5.3.0", "commander": "^12.0.0", "deepmerge": "^4.3.1", - "express": "^4.18.2", - "express-rate-limit": "^7.0.0", "find-up": "^7.0.0", - "http-proxy-middleware": "^2.0.6", "is-mergeable-object": "^1.1.1", + "loader-utils": "^3.3.1", + "mime": "^4.0.4", + "node-fetch": "^3.3.2", "ora": "^8.0.1", "portfinder": "^1.0.32", "pretty-bytes": "^6.1.1", @@ -63,32 +65,44 @@ "vite-tsconfig-paths": "^4.2.0" }, "devDependencies": { + "@equinor/eds-core-react": "^0.41.2", + "@equinor/eds-icons": "^0.21.0", + "@equinor/eds-tokens": "^0.9.2", + "@equinor/eslint-config-fusion-react": "^2.0.1", "@equinor/fusion-framework": "workspace:^", "@equinor/fusion-framework-app": "workspace:^", "@equinor/fusion-framework-module-app": "workspace:^", "@equinor/fusion-framework-module-bookmark": "workspace:^", "@equinor/fusion-framework-module-context": "workspace:^", - "@equinor/fusion-framework-module-http": "workspace:^", + "@equinor/fusion-framework-module-feature-flag": "workspace:^", "@equinor/fusion-framework-module-msal": "workspace:^", "@equinor/fusion-framework-module-navigation": "workspace:^", "@equinor/fusion-framework-module-services": "workspace:^", "@equinor/fusion-framework-react": "workspace:^", "@equinor/fusion-framework-react-components-bookmark": "workspace:^", + "@equinor/fusion-framework-react-components-people-provider": "workspace:^", "@equinor/fusion-framework-react-module-bookmark": "workspace:^", + "@equinor/fusion-observable": "workspace:^", "@equinor/fusion-query": "workspace:^", "@equinor/fusion-react-context-selector": "^0.6.6", "@equinor/fusion-react-progress-indicator": "^0.3.0", "@equinor/fusion-react-side-sheet": "1.3.3", "@equinor/fusion-react-styles": "^0.6.0", + "@equinor/fusion-wc-chip": "^1.2.1", + "@equinor/fusion-wc-person": "^3.0.1", "@material-ui/styles": "^4.11.5", - "@types/express": "^4.17.17", + "@types/adm-zip": "^0.5.0", + "@types/loader-utils": "^2.0.6", "@types/node": "^20.11.14", "@types/react": "^18.2.50", "@types/react-dom": "^18.2.7", + "@types/rollup": "^0.54.0", + "@types/semver": "^7.5.0", + "eslint-plugin-rxjs": "^5.0.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-router-dom": "^6.15.0", - "rollup": "^4.3.0", + "rollup": "^4.22.4", "rxjs": "^7.8.1", "styled-components": "^6.0.7", "typescript": "^5.5.4" diff --git a/packages/cli/src/bin/build-application.ts b/packages/cli/src/bin/build-application.ts index e9cefa6bac..3525cf9ba5 100644 --- a/packages/cli/src/bin/build-application.ts +++ b/packages/cli/src/bin/build-application.ts @@ -5,9 +5,14 @@ import { build } from 'vite'; import { chalk } from './utils/format.js'; import { Spinner } from './utils/spinner.js'; import { loadViteConfig } from './utils/load-vite-config.js'; +import { loadAppManifest } from './utils/load-manifest.js'; -import { resolveAppPackage } from '../lib/app-package.js'; import { type ConfigExecuterEnv } from '../lib/utils/config.js'; +import { resolveAppPackage } from '../lib/app-package.js'; +import { + AppAssetExportPlugin, + createExtensionFilterPattern, +} from '../lib/plugins/app-assets/index.js'; export const buildApplication = async (options: { configSourceFiles?: { @@ -38,10 +43,29 @@ export const buildApplication = async (options: { const packageDirname = dirname(pkg.path); spinner.info(`🏠 ${chalk.blueBright(packageDirname)}`); + spinner.start('resolve application manifest'); + const { manifest } = await loadAppManifest(env, pkg, { + file: configSourceFiles?.manifest, + }); + spinner.succeed(); + const { viteConfig } = await loadViteConfig(env, { file: configSourceFiles?.vite, }); + const includeAssetsPattern = manifest.build?.allowedExtensions + ? createExtensionFilterPattern(manifest.build.allowedExtensions) + : undefined; + + spinner.info('📂', 'Using asset include filter:', chalk.red(includeAssetsPattern)); + + viteConfig.plugins = [ + ...viteConfig.plugins, + AppAssetExportPlugin({ + include: includeAssetsPattern, + }), + ]; + if (library === 'react') { const reactPlugin = await import('@vitejs/plugin-react'); viteConfig.plugins!.push(reactPlugin.default()); @@ -49,10 +73,17 @@ export const buildApplication = async (options: { viteConfig.build.outDir = outDir.trim(); + spinner.attachConsole = true; + + console.log('Building application...'); + const viteBuild = await build(viteConfig); + spinner.attachConsole = false; + return { viteConfig, viteBuild, + pkg, }; }; diff --git a/packages/cli/src/bin/bundle-application.ts b/packages/cli/src/bin/bundle-application.ts index 5705ff49e9..71bf8c2b5e 100644 --- a/packages/cli/src/bin/bundle-application.ts +++ b/packages/cli/src/bin/bundle-application.ts @@ -3,11 +3,10 @@ import AdmZip from 'adm-zip'; import { dirname, resolve } from 'node:path'; import { mkdir } from 'node:fs/promises'; -import { loadPackage } from './utils/load-package.js'; import { chalk, formatByteSize, formatPath } from './utils/format.js'; import { Spinner } from './utils/spinner.js'; import { buildApplication } from './build-application.js'; -import createExportManifest from './create-export-manifest.js'; +import { createBuildManifest } from './create-export-manifest.js'; import { fileExistsSync } from '../lib/utils/file-exists.js'; export const bundleApplication = async (options: { outDir: string; archive: string }) => { @@ -15,16 +14,13 @@ export const bundleApplication = async (options: { outDir: string; archive: stri const spinner = Spinner.Global({ prefixText: chalk.dim('pack') }); - const pkg = await loadPackage(); - spinner.start('build application'); - await buildApplication({ outDir }); + const { pkg } = await buildApplication({ outDir }); spinner.succeed(); spinner.start('generate manifest'); - const manifest = await createExportManifest({ outputFile: `${outDir}/app-manifest.json` }); - spinner.succeed(); - console.log(chalk.dim(JSON.stringify(manifest, undefined, 2))); + const buildManifest = await createBuildManifest({ outputFile: `${outDir}/app-manifest.json` }); + spinner.succeed('generated manifest:', '\n' + JSON.stringify(buildManifest, undefined, 2)); const bundle = new AdmZip(); @@ -34,7 +30,9 @@ export const bundleApplication = async (options: { outDir: string; archive: stri bundle.addLocalFolder(outDir); spinner.info(`added ./${outDir}`); - const licenseFile = resolve(pkg.path, 'LICENSE.md'); + const appDir = dirname(pkg.path); + + const licenseFile = resolve(appDir, 'LICENSE.md'); if (fileExistsSync(licenseFile)) { bundle.addLocalFile(licenseFile); spinner.info(`added ${licenseFile}`); @@ -42,7 +40,7 @@ export const bundleApplication = async (options: { outDir: string; archive: stri spinner.warn(`missing ${licenseFile}`); } - const readmeFile = resolve(pkg.path, 'README.md'); + const readmeFile = resolve(appDir, 'README.md'); if (fileExistsSync(readmeFile)) { bundle.addLocalFile(readmeFile); spinner.info(`added ${readmeFile}`); @@ -54,9 +52,11 @@ export const bundleApplication = async (options: { outDir: string; archive: stri if (!fileExistsSync(dirname(archive))) { await mkdir(dirname(archive), { recursive: true }); } - bundle.writeZip(archive); - spinner.info(formatPath(archive), formatByteSize(archive)); - - spinner.succeed(); + bundle.writeZip(archive); + spinner.succeed( + 'Bundle complete', + formatPath(archive, { relative: true }), + formatByteSize(archive), + ); }; diff --git a/packages/cli/src/bin/create-dev-serve.ts b/packages/cli/src/bin/create-dev-serve.ts index b520b0d083..e8a91c8811 100644 --- a/packages/cli/src/bin/create-dev-serve.ts +++ b/packages/cli/src/bin/create-dev-serve.ts @@ -1,27 +1,35 @@ -import { join } from 'node:path'; -import { readFileSync } from 'node:fs'; -import { assert } from 'node:console'; +import { + createServer, + defineConfig, + mergeConfig, + type LibraryOptions, + type UserConfig, +} from 'vite'; -import { LibraryOptions, createServer } from 'vite'; -import ViteRestart from 'vite-plugin-restart'; +import { assert } from 'node:console'; +import { join, relative } from 'node:path'; import portFinder from 'portfinder'; -import { createDevProxy } from './dev-proxy.js'; +import deepmerge from 'deepmerge/index.js'; + +import ViteRestart from 'vite-plugin-restart'; +import { appProxyPlugin } from '../lib/plugins/app-proxy/app-proxy-plugin.js'; +import { externalPublicPlugin } from '../lib/plugins/external-public/external-public-plugin.js'; + +import { supportedExt, type ConfigExecuterEnv } from '../lib/utils/config.js'; +import { manifestConfigFilename } from '../lib/app-manifest.js'; +import { appConfigFilename } from '../lib/app-config.js'; import { loadAppConfig } from './utils/load-app-config.js'; import { loadViteConfig } from './utils/load-vite-config.js'; -import { loadAppManifest } from './utils/load-manifest.js'; - +import { loadPackage } from './utils/load-package.js'; import { Spinner } from './utils/spinner.js'; import { chalk, formatPath } from './utils/format.js'; +import { loadAppManifest } from './utils/load-manifest.js'; +import { proxyRequestLogger } from './utils/proxy-request-logger.js'; -import { supportedExt, type ConfigExecuterEnv } from '../lib/utils/config.js'; -import { createManifest, manifestConfigFilename } from '../lib/app-manifest.js'; -import { appConfigFilename, createAppConfig } from '../lib/app-config.js'; -import { loadPackage } from './utils/load-package.js'; - -import { rateLimit } from 'express-rate-limit'; +import { type AppManifest } from '@equinor/fusion-framework-module-app'; export const createDevServer = async (options: { portal: string; @@ -34,149 +42,124 @@ export const createDevServer = async (options: { port?: number; library?: 'react'; }) => { - const { configSourceFiles, library, portal, port, devPortalPath } = options; + const { configSourceFiles, library, port, devPortalPath } = options; const spinner = Spinner.Global({ prefixText: chalk.dim('dev-server') }); const pkg = await loadPackage(); - spinner.info(`using portal 🔌${formatPath(portal)} as proxy target`); - const env: ConfigExecuterEnv = { command: 'serve', mode: process.env.NODE_ENV ?? 'development', root: pkg.root, }; + const generateManifest = async () => { + const { manifest } = await loadAppManifest(env, pkg, { + file: configSourceFiles.manifest, + }); + const assetPath = `bundles/apps/${manifest.appKey}/${pkg.packageJson.version}`; + return deepmerge(manifest, { + build: { + assetPath, + configUrl: `${assetPath}/config`, + }, + }) as AppManifest; + }; + + const generateConfig = async () => { + const { config } = await loadAppConfig(env, pkg, { + file: configSourceFiles.app, + }); + return config; + }; + + const { appKey } = await generateManifest(); + /** * Load application manifest * Application might have overridden the `appKey` */ - const manifest = await loadAppManifest(env, pkg, { file: configSourceFiles.manifest }); - const { key: appKey } = manifest.manifest; spinner.info(`resolved application key ${chalk.magenta(appKey)}`); - const { viteConfig, path: viteConfigPath } = await loadViteConfig(env, { + const { viteConfig: baseViteConfig, path: viteConfigPath } = await loadViteConfig(env, { file: configSourceFiles.vite, }); + /** + * Defines the configuration for the development server. + */ + const devServerConfig = defineConfig({ + publicDir: devPortalPath, + appType: 'custom', + server: { + open: `/apps/${appKey}`, + port: port ?? (await portFinder.getPortPromise({ port: 3000 })), + }, + plugins: [ + // Serve the dev portal as static files + externalPublicPlugin(devPortalPath), + // Proxy requests to the app server + appProxyPlugin({ + proxy: { + path: '/apps-proxy', + target: 'https://fusion-s-apps-ci.azurewebsites.net/', + onProxyReq: proxyRequestLogger, + }, + app: { + key: appKey, + version: String(pkg.packageJson.version), + generateConfig, + generateManifest, + }, + }), + // Restart the server when config changes or the dev portal source is updated + ViteRestart({ + restart: [ + 'package.json', + viteConfigPath, + join(relative(process.cwd(), devPortalPath), '/**/*'), + ].filter((x): x is string => !!x), + /** reload the CLI when config changes, note change to APP-KEY need restart */ + reload: [ + ...supportedExt.map((ext) => [appConfigFilename, ext].join('')), + ...supportedExt.map((ext) => [manifestConfigFilename, ext].join('')), + ], + }), + ], + }); + + // Merge the base Vite config with the dev server config + const viteConfig = mergeConfig(devServerConfig, baseViteConfig) as UserConfig; + /** Add library/framework plugins */ if (library === 'react') { const reactPlugin = await import('@vitejs/plugin-react'); - viteConfig.plugins.push(reactPlugin.default()); + viteConfig.plugins!.push(reactPlugin.default()); } - viteConfig.plugins.push( - ViteRestart({ - restart: ['package.json', viteConfigPath].filter((x): x is string => !!x), - /** reload the CLI when config changes, note change to APP-KEY need restart */ - reload: [ - ...supportedExt.map((ext) => [appConfigFilename, ext].join('')), - ...supportedExt.map((ext) => [manifestConfigFilename, ext].join('')), - ], - }), - ); + assert(viteConfig.build?.lib, 'expected vite build to have library defined'); - const vite = await createServer({ ...env, ...viteConfig }); - assert(vite.config.build.lib, 'expected vite build to have library defined'); - const { entry } = vite.config.build.lib as LibraryOptions; - - spinner.info('💾 application entrypoint', formatPath(String(entry))); - - spinner.info('resolving cli internal assets from ', formatPath(devPortalPath)); - - /** add proxy handlers */ - const server = createDevProxy( - { - onConfigResponse: async (slug, message, data) => { - if (slug.appKey === appKey) { - if (message.statusCode === 404) { - const { config: response, path } = await loadAppConfig(env, pkg, { - file: configSourceFiles.app, - }); - return { response, path, statusCode: 200 }; - } else if (data) { - const { config: response, path } = await createAppConfig(env, data, { - file: configSourceFiles.app, - }); - path && spinner.info('created config from ', formatPath(path)); - return { response, path }; - } - } - }, - onManifestResponse: async (slug, message, data) => { - if (slug.appKey === appKey) { - if (message.statusCode === 404) { - const { manifest: response, path } = await loadAppManifest(env, pkg, { - file: configSourceFiles.manifest, - }); - response.entry = `/${entry}`; - return { response, path, statusCode: 200 }; - } else if (data) { - const { manifest: response, path } = await createManifest(env, data, { - file: configSourceFiles.manifest, - }); - response.entry = `/${entry}`; - path && spinner.info('created manifest from ', formatPath(path)); - return { response, path }; - } - } - }, - onManifestListResponse: async (slug, message, data) => { - // TODO: Verify if we should always only return current app or all apps from API + current app. - if (!data) { - const { manifest, path } = await loadAppManifest(env, pkg, { - file: configSourceFiles.manifest, - }); - manifest.entry = `/${entry}`; - return { response: [manifest], path }; - } - let path: string | undefined; - const atIndex = data?.findIndex((manifest) => manifest.key === appKey) ?? -1; - // If existing app, we need to change the entry-point. - if (atIndex > -1) { - data[atIndex].entry = `/${entry}`; - } else { - const { manifest, path: manifestPath } = await loadAppManifest(env, pkg, { - file: configSourceFiles.manifest, - }); - manifest.entry = `/${entry}`; - path = manifestPath; - data.push(manifest); - } - return { response: data, path }; - }, - }, - { - target: portal, - staticAssets: [{ path: devPortalPath }], - }, - ); + const { entry } = viteConfig.build!.lib as LibraryOptions; - /** connect dev server to Vite */ - server.use(vite.middlewares); - - /** redirect all request that miss to index.html, SPA logic */ - server.use( - '*', - async (req, res) => { - // TODO add check if file request - const htmlRaw = readFileSync(join(devPortalPath + '/index.html'), 'utf-8'); - const html = await vite.transformIndexHtml(req.url, htmlRaw); - res.send(html); - }, - rateLimit({ - max: 10, - }), + spinner.info('💾 application entrypoint', formatPath(String(entry), { relative: true })); + + spinner.info( + 'resolving cli internal assets from', + formatPath(String(viteConfig.publicDir), { relative: true }), ); - /** use provided port or resolve available */ - const serverPort = port ?? (await portFinder.getPortPromise({ port: 3000 })); + const vite = await createServer({ ...env, ...viteConfig }); + spinner.start('🚀 start server'); - server.listen(serverPort); - spinner.succeed(); + await vite.listen(); spinner.succeed( '🔗', - chalk.underline.green(new URL(`/apps/${appKey}`, `http://localhost:${serverPort}`).href), + chalk.underline.green( + new URL( + `/apps/${appKey}`, + vite.resolvedUrls?.local[0] ?? `https://localhost:/${vite.config.server.port}`, + ).href, + ), ); }; diff --git a/packages/cli/src/bin/create-export-config.ts b/packages/cli/src/bin/create-export-config.ts index 12c445e019..0bbd212014 100644 --- a/packages/cli/src/bin/create-export-config.ts +++ b/packages/cli/src/bin/create-export-config.ts @@ -1,33 +1,48 @@ import nodeFs from 'node:fs'; import { writeFile } from 'node:fs/promises'; import { dirname } from 'node:path'; +import semverValid from 'semver/functions/valid.js'; import { chalk, formatPath } from './utils/format.js'; import { Spinner } from './utils/spinner.js'; -import { loadPackage } from './utils/load-package.js'; -import { loadAppConfig } from './utils/load-app-config.js'; +import { + getEndpointUrl, + loadAppConfig, + loadPackage, + isAppRegistered, + requireToken, + publishAppConfig, +} from './utils/index.js'; +import type { FusionEnv } from './utils/index.js'; + import { ConfigExecuterEnv } from '../lib/utils/config.js'; +import { resolveAppKey } from '../lib/app-package.js'; +import { exit } from 'node:process'; -export const createExportConfig = async (options?: { +export const createExportConfig = async (options: { command?: ConfigExecuterEnv['command']; configFile?: string; + publish?: string; outputFile?: string; + env: FusionEnv; + service: string; }) => { - const { command = 'build', outputFile } = options ?? {}; + const { command = 'build', outputFile, configFile, publish, env, service } = options; const spinner = Spinner.Global({ prefixText: chalk.dim('config') }); const pkg = await loadPackage(); + const appKey = resolveAppKey(pkg.packageJson); - const env: ConfigExecuterEnv = { + const appEnv: ConfigExecuterEnv = { command, mode: process.env.NODE_ENV ?? 'development', root: pkg.root, }; - const { config } = await loadAppConfig(env, pkg, { - file: options?.configFile, + const { config } = await loadAppConfig(appEnv, pkg, { + file: configFile, }); if (outputFile) { @@ -46,6 +61,86 @@ export const createExportConfig = async (options?: { } else { console.log(config); } + + if (publish) { + spinner.info('Preparing to publishing config'); + + /* Make sure version is valid */ + const version = publish === 'current' ? pkg.packageJson.version : publish; + if (!version || (!semverValid(version) && !['latest', 'preview'].includes(version))) { + spinner.fail( + '🙅‍♂️', + 'Can not publish config to invalid version', + chalk.redBright(version), + '', + ); + exit(1); + } + + /** make sure user has a valid token */ + try { + spinner.info('Validating FUSION_TOKEN'); + + // make sure token exist + requireToken(); + + // call service discovery with token, will throw error if failed + await getEndpointUrl('apps', env, ''); + + spinner.succeed('Found valid FUSION_TOKEN'); + } catch (e) { + const err = e as Error; + spinner.fail(chalk.bgRed(err.message)); + exit(1); + } + + try { + spinner.info('Verifying that App is registered'); + + const state = { endpoint: '' }; + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while verifying app. service-discovery status: ${err.message}`, + ); + } + + await isAppRegistered(state.endpoint, appKey); + spinner.succeed(`${appKey} is registered`); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + try { + spinner.info(`Publishing config to "${appKey}@${version}"`); + + const state = { endpoint: '' }; + try { + state.endpoint = await getEndpointUrl( + `apps/${appKey}/builds/${version}/config`, + env, + service, + ); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while publishig config. service-discovery status: ${err.message}`, + ); + } + + await publishAppConfig(state.endpoint, appKey, config); + spinner.succeed('✅', 'Published config to version', chalk.yellowBright(version)); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + } + return config; }; diff --git a/packages/cli/src/bin/create-export-manifest.ts b/packages/cli/src/bin/create-export-manifest.ts index 1c96e76004..d6e8303c46 100644 --- a/packages/cli/src/bin/create-export-manifest.ts +++ b/packages/cli/src/bin/create-export-manifest.ts @@ -12,7 +12,7 @@ import { loadAppManifest } from './utils/load-manifest.js'; import { ConfigExecuterEnv } from '../lib/utils/config.js'; import { loadPackage } from './utils/load-package.js'; import { dirname } from 'node:path'; -import { AppManifest } from '../lib/app-manifest.js'; +import type { AppManifest } from '@equinor/fusion-framework-module-app'; // TODO why do we do this??? can`t backend parse semver? export const normalizeVersion = (version: string) => { @@ -23,27 +23,59 @@ export const normalizeVersion = (version: string) => { return { major, minor, patch }; }; -type AppManifestExport = Omit & { - version: { - major: number; - minor: number; - patch: number; - }; -}; - -export const createExportManifest = async (options?: { +type CreateManifestOptions = { command?: ConfigExecuterEnv['command']; configFile?: string; outputFile?: string; -}) => { - const { command = 'build', outputFile } = options ?? {}; + onlyBuild?: boolean; +}; - const spinner = Spinner.Global({ prefixText: chalk.dim('manifest') }); +export const createAppManifest = async (options?: CreateManifestOptions) => { + Spinner.Global({ prefixText: chalk.dim('app manifest') }); + const manifest = await createManifest(options); + if (options?.outputFile) { + await writeManifestToDisk(manifest, options.outputFile); + } else { + console.log(JSON.stringify(manifest, undefined, 2)); + } + return manifest; +}; + +export const createBuildManifest = async (options?: CreateManifestOptions) => { + Spinner.Global({ prefixText: chalk.dim('build manifest') }); + const { build } = await createManifest(options); + if (options?.outputFile) { + await writeManifestToDisk(build, options.outputFile); + } else { + console.log(JSON.stringify(build, undefined, 2)); + } + return build; +}; +const writeManifestToDisk = async ( + content: AppManifest | AppManifest['build'], + outputFile: string, +): Promise => { + const spinner = Spinner.Clone(); + spinner.start(`Exporting manifest to ${formatPath(outputFile)}`); + try { + const dir = dirname(outputFile).trim(); + if (!nodeFs.existsSync(dirname(outputFile))) { + nodeFs.mkdirSync(dir, { recursive: true }); + } + await writeFile(outputFile, JSON.stringify(content)); + spinner.succeed(); + } catch (err) { + spinner.fail(); + throw err; + } +}; + +const createManifest = async (options?: CreateManifestOptions): Promise => { const pkg = await loadPackage(); const env: ConfigExecuterEnv = { - command, + command: options?.command ?? 'build', mode: process.env.NODE_ENV ?? 'development', root: pkg.root, }; @@ -52,28 +84,5 @@ export const createExportManifest = async (options?: { file: options?.configFile, }); - const manifestExport: AppManifestExport = { - ...manifest, - version: normalizeVersion(manifest.version), - }; - - if (outputFile) { - spinner.start(`outputting manifest to ${formatPath(outputFile)}`); - try { - const dir = dirname(outputFile).trim(); - if (!nodeFs.existsSync(dirname(outputFile))) { - nodeFs.mkdirSync(dir, { recursive: true }); - } - await writeFile(outputFile, JSON.stringify(manifestExport)); - spinner.succeed(); - } catch (err) { - spinner.fail(); - throw err; - } - } else { - console.log(manifestExport); - } - return manifestExport; + return manifest; }; - -export default createExportManifest; diff --git a/packages/cli/src/bin/dev-portal/config.ts b/packages/cli/src/bin/dev-portal/config.ts index 978a2f1c8e..da7d64e022 100644 --- a/packages/cli/src/bin/dev-portal/config.ts +++ b/packages/cli/src/bin/dev-portal/config.ts @@ -11,15 +11,20 @@ import { } from '@equinor/fusion-framework-module-feature-flag/plugins'; export const configure = async (config: FrameworkConfigurator) => { - config.logger.level = 0; - config.configureServiceDiscovery({ client: { - baseUri: String(new URL('/_discovery/environments/current', import.meta.url)), + baseUri: + 'https://discovery.fusion.equinor.com/service-registry/environments/ci/services', defaultScopes: ['5a842df8-3238-415d-b168-9f16a6a6031b/.default'], }, }); + // Add custom client for app + config.configureHttpClient('app', { + baseUri: new URL('/apps-proxy/', window.location.href).href, + defaultScopes: ['5a842df8-3238-415d-b168-9f16a6a6031b/.default'], + }); + config.configureMsal( { tenantId: '3aa4a235-b6e2-48d5-9195-7fcf05b459b0', diff --git a/packages/cli/src/bin/dev-proxy.ts b/packages/cli/src/bin/dev-proxy.ts deleted file mode 100644 index 8166dd4bf3..0000000000 --- a/packages/cli/src/bin/dev-proxy.ts +++ /dev/null @@ -1,155 +0,0 @@ -import express, { type Request, type Express } from 'express'; - -import { - createProxyMiddleware, - responseInterceptor, - type Options as ProxyOptions, -} from 'http-proxy-middleware'; - -import { type AppManifest } from '../lib/app-manifest.js'; -import { type IncomingMessage } from 'node:http'; -import { type AppConfig } from '../lib/app-config.js'; -import { Spinner } from './utils/spinner.js'; -import chalk, { formatPath } from './utils/format.js'; - -type ProxyHandlerResult = { response?: T; statusCode?: number; path?: string } | void; -type ProxyHandlerReturn = Promise> | ProxyHandlerResult; - -export interface ProxyHandler { - onManifestListResponse( - slug: object, - message: IncomingMessage, - data?: Array, - ): ProxyHandlerReturn>; - onManifestResponse( - slug: { appKey: string }, - message: IncomingMessage, - data?: AppManifest, - ): ProxyHandlerReturn; - onConfigResponse( - slug: { appKey: string }, - message: IncomingMessage, - data?: AppConfig, - ): ProxyHandlerReturn; -} - -const createResponseInterceptor = ( - cb: (args: TArgs, message: IncomingMessage, data?: TType) => ProxyHandlerReturn, -) => { - return responseInterceptor(async (responseBuffer, proxyRes, req, res) => { - res.setHeader('x-proxy-status-message', proxyRes.statusMessage ?? ''); - res.setHeader('x-proxy-status-code', proxyRes.statusCode ?? ''); - const { response, statusCode, path } = - (await Promise.resolve( - cb( - (req as Request).params, - proxyRes, - // might check?? - Number(proxyRes.statusCode) < 400 && - JSON.parse(responseBuffer.toString('utf8')), - ), - )) ?? {}; - if (statusCode) { - res.statusCode = statusCode; - } - if (response) { - path && res.setHeader('x-interceptor-handler', path); - return JSON.stringify(response); - } - return responseBuffer; - }); -}; - -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export const createDevProxy = ( - handler: ProxyHandler, - options: Pick & { - staticAssets?: { path: string; options?: Parameters[1] }[]; - }, -): Express => { - const proxyOptions: ProxyOptions = Object.assign( - { - changeOrigin: true, - selfHandleResponse: true, - onProxyReq: (proxyReq) => { - const spinner = Spinner.Clone(); - spinner.ora.suffixText = formatPath( - [proxyReq.protocol, '//', proxyReq.host, proxyReq.path].join(''), - ); - spinner.start('proxy request'); - proxyReq.on('response', (res) => { - if (Number(res.statusCode) < 400) { - spinner.succeed(); - } else { - spinner.warn(chalk.yellow(res.statusMessage ?? `${res.statusCode} `)); - } - spinner.stop(); - }); - proxyReq.on('error', () => { - spinner.fail(); - }); - }, - } satisfies ProxyOptions, - options, - ); - - const app = express(); - - app.disable('x-powered-by'); - - options.staticAssets?.forEach((asset) => { - app.use(express.static(asset.path, asset.options)); - }); - - app.use( - createProxyMiddleware('/_discovery/environments/current', { - ...proxyOptions, - onProxyRes: responseInterceptor(async (responseBuffer, _proxyRes, req) => { - const response = JSON.parse(responseBuffer.toString('utf8')); - const services = response.services - .filter((x: { key: string }) => x.key !== 'app') - .map((service: object) => ({ - ...service, - scopes: [response.clientId + '/.default'], - environment: 'DEVELOPMENT', - })); - services.push({ - key: 'app', - uri: new URL('/', req.headers.referer).href, - scopes: [response.clientId + '/.default'], - }); - - return JSON.stringify(services); - }), - }), - ); - - app.get( - '/api/apps/:appKey/config', - // '/api/widget/:appKey/config', - createProxyMiddleware('/api/apps/*/config', { - ...proxyOptions, - onProxyRes: createResponseInterceptor(handler.onConfigResponse), - }), - ); - - app.get( - '/api/apps/:appKey', - createProxyMiddleware('/api/apps/*', { - ...proxyOptions, - onProxyRes: createResponseInterceptor(handler.onManifestResponse), - }), - ); - - app.get( - '/api/apps', - createProxyMiddleware('/api/apps', { - ...proxyOptions, - onProxyRes: createResponseInterceptor(handler.onManifestListResponse), - }), - ); - - return app; -}; - -export default createDevProxy; diff --git a/packages/cli/src/bin/main.app.ts b/packages/cli/src/bin/main.app.ts index 271f37e08b..67550d1269 100644 --- a/packages/cli/src/bin/main.app.ts +++ b/packages/cli/src/bin/main.app.ts @@ -2,9 +2,12 @@ import { Command } from 'commander'; import { createDevServer } from './create-dev-serve.js'; import { buildApplication } from './build-application.js'; +import { publishApplication } from './publish-application.js'; +import { uploadApplication } from './upload-application.js'; +import { tagApplication } from './tag-application.js'; import { formatPath, chalk } from './utils/format.js'; -import createExportManifest from './create-export-manifest.js'; +import { createAppManifest, createBuildManifest } from './create-export-manifest.js'; import { bundleApplication } from './bundle-application.js'; import { createExportConfig } from './create-export-config.js'; import { fileURLToPath } from 'node:url'; @@ -16,7 +19,7 @@ export default (program: Command) => { .description('Tooling for developing applications build on Fusion Framework'); app.command('dev') - .description('Create a development server') + .description('Start development server for application') .option('-p, --port ', 'dev-server port') .option('-P, --portal ', 'fusion portal host') .option( @@ -64,7 +67,19 @@ export default (program: Command) => { }); }); + app.command('manifest') + .description('Generate manifest') + .option('-o, --output ', 'output file') + .option('-c, --config ', 'manifest config file') + .action((opt) => { + createAppManifest({ + outputFile: opt.output, + configFile: opt.config, + }); + }); + app.command('build') + .description('Builds application') .option('-o, --outDir, ', 'output directory of package', 'dist') .option( '-c, --config ', @@ -92,30 +107,114 @@ export default (program: Command) => { }); }); - app.command('config') + app.command('build-config') + .description('Generate config') .option('-o, --output ', 'output file') .option('-c, --config ', 'application config file') + .option( + '-p, --publish ', + `Publish app config to version [${chalk.yellowBright('(semver | current | latest | preview)')}]`, + 'current', + ) + .requiredOption( + '-e, --env, ', + 'Fusion environment to build api urls from. used when publishing config.', + ) + .option( + '-s, --service, ', + 'Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands. the --env parameter is ignored when set', + ) .action((opt) => { createExportConfig({ outputFile: opt.output, configFile: opt.config, + publish: opt.publish, + env: opt.env, + service: opt.service, }); }); - app.command('manifest') + + app.command('build-manifest') + .description('Generate manifest') .option('-o, --output ', 'output file') .option('-c, --config ', 'manifest config file') .action((opt) => { - createExportManifest({ + createBuildManifest({ outputFile: opt.output, configFile: opt.config, }); }); - app.command('pack') + app.command('build-pack') + .description('Create distributable app bundle of the application') .option('-o, --outDir, ', 'output directory of package', 'dist') .option('-a, --archive, ', 'output filename', 'app-bundle.zip') .action(async (opt) => { const { outDir, archive } = opt; bundleApplication({ archive, outDir }); }); + + app.command('build-publish') + .description('Publish application to app api') + .option( + '-t, --tag, ', + `Tagname to publish this build as [${chalk.yellowBright('(latest | preview)')}]`, + 'latest', + ) + .requiredOption( + '-e, --env, ', + 'Fusion environment to build api urls from', + ) + .option( + '-s, --service, ', + 'Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands. the --env parameter is ignored when set', + ) + .action(async (opt) => { + const { tag, env, service } = opt; + publishApplication({ tag, env, service }); + }); + + app.command('build-upload') + .description('Upload packaged app bundle to app api') + .option( + '-b, --bundle, ', + 'The packaged app bundle file to upload', + 'app-bundle.zip', + ) + .requiredOption( + '-e, --env, ', + 'Fusion environment to build api urls from', + ) + .option( + '-s, --service, ', + 'Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands. the --env parameter is ignored when set', + ) + .action(async (opt) => { + const { bundle, env, service } = opt; + uploadApplication({ bundle, env, service }); + }); + + app.command('build-tag') + .description('Tag a published version') + .option( + '-t, --tag, ', + `Tag the published version with tagname [${chalk.yellowBright('(latest | preview)')}]`, + 'latest', + ) + .requiredOption( + '-v, --version, ', + 'Version number to tag, must be a published version number', + ) + .requiredOption( + '-e, --env, ', + 'Fusion environment to build api urls from', + ) + .option( + '-s, --service, ', + 'Define uri to custom app service. You can also define the env variable CUSTOM_APPAPI to be used on all publish commands. the --env parameter is ignored when set', + ) + .action(async (opt) => { + const { tag, version, env, service } = opt; + tagApplication({ tag, version, env, service }); + }); }; diff --git a/packages/cli/src/bin/main.ts b/packages/cli/src/bin/main.ts index 88b2213b82..9ae79f013c 100644 --- a/packages/cli/src/bin/main.ts +++ b/packages/cli/src/bin/main.ts @@ -16,8 +16,10 @@ import { Command } from 'commander'; const program = new Command(); program.name(pkg.packageJson.name); -program.description(pkg.packageJson.description ?? 'CLI for Fusion Framework'); -program.version(pkg.packageJson.version); +program.description( + `fusion-framework-cli@${pkg.packageJson.version}.\nCLI for the Fusion Framework`, +); +program.version(pkg.packageJson.version, '-V, --vers', 'CLI version'); /** add app commands */ import app from './main.app.js'; diff --git a/packages/cli/src/bin/publish-application.ts b/packages/cli/src/bin/publish-application.ts new file mode 100644 index 0000000000..d632e9b0a9 --- /dev/null +++ b/packages/cli/src/bin/publish-application.ts @@ -0,0 +1,138 @@ +import { chalk } from './utils/format.js'; +import { Spinner } from './utils/spinner.js'; +import { bundleApplication } from './bundle-application.js'; +import { resolveAppPackage, resolveAppKey } from '../lib/app-package.js'; + +import { + isAppRegistered, + getEndpointUrl, + requireToken, + tagAppBundle, + uploadAppBundle, +} from './utils/index.js'; +import type { FusionEnv } from './utils/index.js'; + +import { exit } from 'node:process'; + +export const publishApplication = async (options: { + tag: string; + env: FusionEnv; + service: string; +}) => { + const { tag, env, service } = options; + + const spinner = Spinner.Global({ prefixText: chalk.dim('Publish') }); + + try { + spinner.info('Validating FUSION_TOKEN'); + + // make sure token exist + requireToken(); + + // call service discovery with token, will throw error if failed + await getEndpointUrl('apps', env, ''); + + spinner.succeed('Found valid FUSION_TOKEN'); + } catch (e) { + const err = e as Error; + spinner.fail(chalk.bgRed(err.message)); + exit(1); + } + + const pkg = await resolveAppPackage(); + const appKey = resolveAppKey(pkg.packageJson); + + try { + spinner.info('Verifying that App is registered'); + const state = { endpoint: '' }; + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while verifying app is registered. service-discovery status: ${err.message}`, + ); + } + + await isAppRegistered(state.endpoint, appKey); + spinner.succeed(`${appKey} is registered`); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + const bundle = 'app-bundle.zip'; + + /* Zip app bundle */ + spinner.info('Creating zip bundle'); + + await bundleApplication({ + archive: bundle, + outDir: 'dist', + }); + + const state = { + uploadedBundle: { version: '' }, + endpoint: '', + }; + + spinner.info(`Publishing app: "${appKey}" with tag: "${tag}"`); + + /* Upload app bundle */ + try { + spinner.info( + `Uploading bundle ${chalk.yellowBright(bundle)} to appKey ${chalk.yellowBright(appKey)}`, + ); + + try { + state.endpoint = await getEndpointUrl(`bundles/apps/${appKey}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while uploading app bundle. service-discovery status: ${err.message}`, + ); + } + + spinner.info(`Posting bundle to => ${state.endpoint}`); + + state.uploadedBundle = await uploadAppBundle(state.endpoint, bundle); + + spinner.succeed( + '✅', + `Uploaded bundle: "${chalk.greenBright(bundle)}" with version: ${chalk.greenBright(state.uploadedBundle.version)}"`, + ); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + try { + spinner.info(`Tagging ${state.uploadedBundle.version} with ${tag}`); + + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}/tags/${tag}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while tagging app. service-discovery status: ${err.message}`, + ); + } + + const tagged = await tagAppBundle(state.endpoint, state.uploadedBundle.version); + spinner.succeed( + '✅', + `Tagged version ${chalk.greenBright(tagged.version)} with ${chalk.greenBright(tagged.tagName)}`, + ); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + spinner.succeed( + '⭐️', + `Published app: "${chalk.greenBright(appKey)}" version: "${chalk.greenBright(state.uploadedBundle.version)}" with tagg: "${chalk.greenBright(tag)}"`, + ); +}; diff --git a/packages/cli/src/bin/tag-application.ts b/packages/cli/src/bin/tag-application.ts new file mode 100644 index 0000000000..47a0a6c4b0 --- /dev/null +++ b/packages/cli/src/bin/tag-application.ts @@ -0,0 +1,92 @@ +import { exit } from 'node:process'; +import { chalk } from './utils/format.js'; +import { Spinner } from './utils/spinner.js'; +import { resolveAppPackage, resolveAppKey } from '../lib/app-package.js'; +import { isAppRegistered, getEndpointUrl, requireToken, tagAppBundle } from './utils/index.js'; +import type { FusionEnv } from './utils/index.js'; + +enum Tags { + preview = 'preview', + latest = 'latest', +} + +export const tagApplication = async (options: { + tag: keyof typeof Tags; + version: string; + env: FusionEnv; + service: string; +}) => { + const { tag, version, env, service } = options; + + const spinner = Spinner.Global({ prefixText: chalk.dim('Tag') }); + + if (!Object.values(Tags).includes(tag as Tags)) { + spinner.fail('😞', `Tag must match (${Tags.latest} | ${Tags.preview})`); + exit(1); + } + + /** make sure user has a valid token */ + try { + spinner.info('Validating FUSION_TOKEN'); + + // make sure token exist + requireToken(); + + // call service discovery with token, will throw error if failed + await getEndpointUrl('apps', env, ''); + + spinner.succeed('Found valid FUSION_TOKEN'); + } catch (e) { + const err = e as Error; + spinner.fail(chalk.bgRed(err.message)); + exit(1); + } + + const pkg = await resolveAppPackage(); + const appKey = resolveAppKey(pkg.packageJson); + + try { + spinner.info('Verifying that App is registered'); + const state = { endpoint: '' }; + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while verifying app is registered. service-discovery status: ${err.message}`, + ); + } + + await isAppRegistered(state.endpoint, appKey); + spinner.succeed(`${appKey} is registered`); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + try { + spinner.info(`Tagging "${appKey}@${version}" with: "${tag}"`); + const state = { endpoint: '' }; + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}/tags/${tag}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while tagging app. service-discovery status: ${err.message}`, + ); + } + + const tagged = await tagAppBundle(state.endpoint, version); + spinner.succeed( + '✅', + `Tagged app: "${chalk.greenBright(appKey)}"`, + `version: "${chalk.greenBright(tagged.version)}"`, + `with tag: "${chalk.greenBright(tagged.tagName)}"`, + ); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } +}; diff --git a/packages/cli/src/bin/upload-application.ts b/packages/cli/src/bin/upload-application.ts new file mode 100644 index 0000000000..2c29f1c237 --- /dev/null +++ b/packages/cli/src/bin/upload-application.ts @@ -0,0 +1,83 @@ +import { exit } from 'node:process'; +import { Spinner } from './utils/spinner.js'; +import { chalk } from './utils/format.js'; +import { resolveAppPackage, resolveAppKey } from '../lib/app-package.js'; +import { isAppRegistered, getEndpointUrl, requireToken, uploadAppBundle } from './utils/index.js'; +import type { FusionEnv } from './utils/index.js'; + +export const uploadApplication = async (options: { + bundle: string; + env: FusionEnv; + service: string; +}) => { + const { bundle, env, service } = options; + + const spinner = Spinner.Global({ prefixText: chalk.dim('Upload') }); + + try { + spinner.info('Validating FUSION_TOKEN'); + + // make sure token exist + requireToken(); + + // call service discovery with token, will throw error if failed + await getEndpointUrl('apps', env, ''); + + spinner.succeed('Found valid FUSION_TOKEN'); + } catch (e) { + const err = e as Error; + spinner.fail(chalk.bgRed(err.message)); + exit(1); + } + + /* get package.json */ + const pkg = await resolveAppPackage(); + const appKey = resolveAppKey(pkg.packageJson); + + try { + spinner.info('Verifying that App is registered'); + const state = { endpoint: '' }; + + try { + state.endpoint = await getEndpointUrl(`apps/${appKey}`, env, service); + } catch (e) { + const err = e as Error; + throw new Error( + `Could not get endpoint from service discovery while verifying app. service-discovery status: ${err.message}`, + ); + } + + await isAppRegistered(state.endpoint, appKey); + spinner.succeed(`${appKey} is registered`); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } + + /* Upload app bundle */ + try { + spinner.info( + `Uploading bundle ${chalk.yellowBright(bundle)} to appKey ${chalk.yellowBright(appKey)}`, + ); + + const endpoint = await getEndpointUrl(`bundles/apps/${appKey}`, env, service); + if (!endpoint) { + throw new Error('Could not get endpoint from service discovery'); + } + + spinner.info(`Posting bundle to => ${endpoint}`); + + const uploadedBundle = await uploadAppBundle(endpoint, bundle); + + spinner.succeed( + '✅', + `Uploaded app: "${chalk.greenBright(appKey)}"`, + `Version: "${chalk.greenBright(uploadedBundle.version)}"`, + ); + } catch (e) { + const err = e as Error; + spinner.fail('🙅‍♂️', chalk.bgRed(err.message)); + exit(1); + } +}; diff --git a/packages/cli/src/bin/utils/execute-commant.ts b/packages/cli/src/bin/utils/execute-command.ts similarity index 100% rename from packages/cli/src/bin/utils/execute-commant.ts rename to packages/cli/src/bin/utils/execute-command.ts diff --git a/packages/cli/src/bin/utils/format.ts b/packages/cli/src/bin/utils/format.ts index 74973cd4c0..0b43983d73 100644 --- a/packages/cli/src/bin/utils/format.ts +++ b/packages/cli/src/bin/utils/format.ts @@ -8,7 +8,9 @@ import prettyBytes from 'pretty-bytes'; export { chalk }; export const formatPath = (path: string, opt?: { relative?: boolean; cwd?: string }) => { - return chalk.blueBright(opt?.relative ? relative(opt?.cwd ?? process.cwd(), path) : path); + return chalk.blueBright( + opt?.relative ? './' + relative(opt?.cwd ?? process.cwd(), path) : path, + ); }; export const formatByteSize = (input: string | number): string => { diff --git a/packages/cli/src/bin/utils/getEndpointUrl.ts b/packages/cli/src/bin/utils/getEndpointUrl.ts new file mode 100644 index 0000000000..719cf0fa95 --- /dev/null +++ b/packages/cli/src/bin/utils/getEndpointUrl.ts @@ -0,0 +1,61 @@ +export type FusionEnv = 'ci' | 'fqa' | 'tr' | 'fprd'; +import fetch from 'node-fetch'; + +/** + * Retreive full endpoint URI to env in service-discovery + * @param endpoint The endpoint to call in+ uri + * @param fusionEnv The Fusion env to get uri for + * @param service Custom service uri to use insted of Fusion + * @param version The version of the api to use + * @returns The uri with endpoint + */ +export const getEndpointUrl = async ( + endpoint: string, + fusionEnv: FusionEnv, + service: string, + version: string = '1.0', +): Promise => { + const { CUSTOM_APPAPI, FUSION_CLI_ENV, FUSION_TOKEN } = process.env; + + /* use consumer provided api url */ + if (service || CUSTOM_APPAPI) { + return service ?? CUSTOM_APPAPI; + } + + /* Env has changed get new api url */ + if (FUSION_CLI_ENV !== fusionEnv || !process.env.FUSION_CLI_APPAPI) { + process.env.FUSION_CLI_ENV = fusionEnv; + + const requestService = await fetch( + `https://discovery.fusion.equinor.com/service-registry/environments/${fusionEnv}/services/apps`, + { + headers: { + Authorization: `Bearer ${FUSION_TOKEN}`, + }, + }, + ); + + if (requestService.status === 401) { + throw new Error( + `The provided FUSION_TOKEN is not valid. Refresh your token and try again.`, + ); + } + + if (!requestService.ok) { + const response = await requestService.json(); + console.log(response); + throw new Error( + `Failed getEndpointUrl from service-discovery. HTTP status: ${requestService.status} - ${requestService.statusText}`, + ); + } + + const responseService = (await requestService.json()) as { uri: string }; + process.env.FUSION_CLI_APPAPI = responseService.uri; + } + + const uri = new URL(`${process.env.FUSION_CLI_APPAPI}/${endpoint}`); + uri.searchParams.set('api-version', version); + + /* return fresh/cached endpoint url */ + return uri.href; +}; diff --git a/packages/cli/src/bin/utils/index.ts b/packages/cli/src/bin/utils/index.ts new file mode 100644 index 0000000000..54cd5efd57 --- /dev/null +++ b/packages/cli/src/bin/utils/index.ts @@ -0,0 +1,18 @@ +export { getEndpointUrl } from './getEndpointUrl.js'; +export type { FusionEnv } from './getEndpointUrl.js'; +export { requireToken } from './requireToken.js'; +export { isAppRegistered } from './isAppRegistered.js'; + +export { loadAppConfig } from './load-app-config.js'; +export { loadAppManifest } from './load-manifest.js'; +export { loadPackage } from './load-package.js'; +export { loadViteConfig } from './load-vite-config.js'; + +export { formatPath, formatByteSize } from './format.js'; +export { executeCommand } from './execute-command.js'; + +export { publishAppConfig } from './publishAppConfig.js'; +export { tagAppBundle } from './tagAppBundle.js'; +export { uploadAppBundle } from './uploadAppBundle.js'; + +export { Spinner } from './spinner.js'; diff --git a/packages/cli/src/bin/utils/isAppRegistered.ts b/packages/cli/src/bin/utils/isAppRegistered.ts new file mode 100644 index 0000000000..951e24f4f0 --- /dev/null +++ b/packages/cli/src/bin/utils/isAppRegistered.ts @@ -0,0 +1,29 @@ +/** + * Make sure the app is registerred in the app-service + * @param endpoint The endpoint to make a call to + * @param appKey The appkey to check for + * @returns response object as json + */ +export const isAppRegistered = async (endpoint: string, appKey: string) => { + const requestApp = await fetch(endpoint, { + headers: { + Authorization: `Bearer ${process.env.FUSION_TOKEN}`, + }, + }); + + if (requestApp.status === 404) { + throw new Error( + `The appkey '${appKey}' is not registered, visit the app-admin app and register the application there.`, + ); + } + + if (!requestApp.ok) { + const response = await requestApp.json(); + console.log(response); + throw new Error( + `Could not connect to apps-service. HTTP status ${requestApp.status}, ${requestApp.statusText}`, + ); + } + + return await requestApp.json(); +}; diff --git a/packages/cli/src/bin/utils/load-app-config.ts b/packages/cli/src/bin/utils/load-app-config.ts index 13f2719307..bcaab1de51 100644 --- a/packages/cli/src/bin/utils/load-app-config.ts +++ b/packages/cli/src/bin/utils/load-app-config.ts @@ -15,6 +15,9 @@ export const loadAppConfig = async ( const spinner = Spinner.Current; try { spinner.start('create application configuration'); + spinner.info( + `generating config with ${chalk.red.dim(env.command)} command in ${chalk.green.dim(env.mode)} mode`, + ); const baseAppConfig = createAppConfigFromPackage(pkg); const appConfig = await createAppConfig(env, baseAppConfig, { file: options?.file }); spinner.succeed(); diff --git a/packages/cli/src/bin/utils/load-manifest.ts b/packages/cli/src/bin/utils/load-manifest.ts index d1119c9c91..21bb781799 100644 --- a/packages/cli/src/bin/utils/load-manifest.ts +++ b/packages/cli/src/bin/utils/load-manifest.ts @@ -2,6 +2,9 @@ import { Spinner } from './spinner.js'; import { formatPath, chalk } from './format.js'; import { createManifest, createManifestFromPackage } from '../../lib/app-manifest.js'; + +import type { AppManifest } from '@equinor/fusion-framework-module-app'; + import { type ConfigExecuterEnv } from '../../lib/utils/config.js'; import { type ResolvedAppPackage } from '../../lib/app-package.js'; @@ -11,22 +14,31 @@ export const loadAppManifest = async ( options?: { file?: string; }, -) => { - const spinner = Spinner.Current; +): Promise<{ manifest: AppManifest; path?: string }> => { + const spinner = Spinner.Clone(); try { spinner.start('create application manifest'); const baseManifest = await createManifestFromPackage(pkg); spinner.info('created application manifest from package.json'); + // TODO - this need to come from the config + if (env.command !== 'serve') { + baseManifest.build!.entryPoint = + pkg.packageJson.type === 'module' ? 'app-bundle.js' : 'app-bundle.mjs'; + } + + spinner.info( + `generating manifest with ${chalk.red.dim(env.command)} command in ${chalk.green.dim(env.mode)} mode`, + ); + const manifest = await createManifest(env, baseManifest, { file: options?.file }); - spinner.succeed(); if (manifest.path) { - spinner.info( - `generating manifest from ${formatPath(manifest.path, { relative: true })}`, + spinner.succeed( + `Created manifest from ${formatPath(manifest.path, { relative: true })}`, ); } else { - spinner.info(chalk.dim('no local manifest config applied, using default generated')); + spinner.succeed(chalk.dim('no local manifest config applied, using default generated')); } return manifest; diff --git a/packages/cli/src/bin/utils/proxy-request-logger.ts b/packages/cli/src/bin/utils/proxy-request-logger.ts new file mode 100644 index 0000000000..e24d3a1d8f --- /dev/null +++ b/packages/cli/src/bin/utils/proxy-request-logger.ts @@ -0,0 +1,36 @@ +import { type ClientRequest } from 'http'; +import { Spinner } from './spinner.js'; +import { formatPath, chalk } from './format.js'; + +/** + * Logs the status of a proxy request using a spinner. + * + * @param proxyReq - The proxy request to log. + * + * The function attaches event listeners to the proxy request to handle + * 'response' and 'error' events. It uses a spinner to indicate the status + * of the request: + * - On a successful response (status code < 400), the spinner succeeds. + * - On a response with a status code >= 400, the spinner warns with the status message. + * - On an error, the spinner fails. + */ +export const proxyRequestLogger = (proxyReq: ClientRequest) => { + const spinner = Spinner.Clone(); + spinner.ora.suffixText = formatPath( + [proxyReq.protocol, '//', proxyReq.host, proxyReq.path].join(''), + ); + spinner.start('proxy request'); + proxyReq.on('response', (res) => { + if (Number(res.statusCode) < 400) { + spinner.succeed(); + } else { + spinner.warn(chalk.yellow(res.statusMessage ?? `${res.statusCode} `)); + } + spinner.stop(); + }); + proxyReq.on('error', () => { + spinner.fail(); + }); +}; + +export default proxyRequestLogger; diff --git a/packages/cli/src/bin/utils/publishAppConfig.ts b/packages/cli/src/bin/utils/publishAppConfig.ts new file mode 100644 index 0000000000..e955412ee9 --- /dev/null +++ b/packages/cli/src/bin/utils/publishAppConfig.ts @@ -0,0 +1,33 @@ +import type { AppConfig } from '@equinor/fusion-framework-module-app'; + +/** + * Publishes app config to the apps-service endpoint + * @param endpoint string The endpoint to upload to + * @param appKey The application key + * @param config Object with app config + * @returns HTTP response as json + */ +export const publishAppConfig = async (endpoint: string, appKey: string, config: AppConfig) => { + const requestConfig = await fetch(endpoint, { + method: 'PUT', + body: JSON.stringify(config), + headers: { + Authorization: `Bearer ${process.env.FUSION_TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + + if (requestConfig.status === 410) { + throw new Error( + `App ${appKey} is deleted from apps-service. HTTP status ${requestConfig.status}, ${requestConfig.statusText}`, + ); + } else if (!requestConfig.ok || requestConfig.status >= 400) { + const response = await requestConfig.json(); + console.log(response); + throw new Error( + `Failed to upload config. HTTP status ${requestConfig.status}, ${requestConfig.statusText}`, + ); + } + + return requestConfig.json(); +}; diff --git a/packages/cli/src/bin/utils/requireToken.ts b/packages/cli/src/bin/utils/requireToken.ts new file mode 100644 index 0000000000..4d5826a07a --- /dev/null +++ b/packages/cli/src/bin/utils/requireToken.ts @@ -0,0 +1,10 @@ +/** + * Make sure the user has a valid azure token. + */ +export const requireToken = () => { + if (!process?.env?.FUSION_TOKEN) { + throw new Error( + 'Missing required environment variable FUSION_TOKEN. Please set it before running this command.', + ); + } +}; diff --git a/packages/cli/src/bin/utils/spinner.ts b/packages/cli/src/bin/utils/spinner.ts index 0205623083..e4a4f462e5 100644 --- a/packages/cli/src/bin/utils/spinner.ts +++ b/packages/cli/src/bin/utils/spinner.ts @@ -3,6 +3,8 @@ import ora, { Options, type Ora } from 'ora'; const parseArgs = (args: string[]): string | undefined => args.length ? args.join(' ') : undefined; +const originalConsole = console; + export class Spinner { #ora: Ora; @@ -10,6 +12,15 @@ export class Spinner { return this.#ora; } + set attachConsole(value: boolean) { + if (value) { + console.log = this.info.bind(this); + console.info = this.info.bind(this); + } else { + console = originalConsole; + } + } + static Global(options?: Options) { _spinner = new Spinner(options); return _spinner; diff --git a/packages/cli/src/bin/utils/tagAppBundle.ts b/packages/cli/src/bin/utils/tagAppBundle.ts new file mode 100644 index 0000000000..21a1f1a593 --- /dev/null +++ b/packages/cli/src/bin/utils/tagAppBundle.ts @@ -0,0 +1,32 @@ +/** + * Send request to apps-service to tag a bundle. + * @param endpoint string The endpoint to send request to. + * @param version string The version to tag the bundle with. + * @returns Response object as json. + */ +export const tagAppBundle = async (endpoint: string, version: string) => { + const requestTag = await fetch(endpoint, { + method: 'PUT', + body: JSON.stringify({ version }), + headers: { + Authorization: `Bearer ${process.env.FUSION_TOKEN}`, + 'Content-Type': 'application/json', + }, + }); + + if (requestTag.status === 404) { + throw new Error( + `Failed to tag bundle, make sure version ${version} exist. HTTP status ${requestTag.status} - ${requestTag.statusText}`, + ); + } + + if (requestTag.status !== 200) { + const response = await requestTag.json(); + console.log(response); + throw new Error( + `Failed to tag bundle. HTTP status ${requestTag.status}, ${requestTag.statusText}`, + ); + } + + return requestTag.json(); +}; diff --git a/packages/cli/src/bin/utils/uploadAppBundle.ts b/packages/cli/src/bin/utils/uploadAppBundle.ts new file mode 100644 index 0000000000..c3fcf07c86 --- /dev/null +++ b/packages/cli/src/bin/utils/uploadAppBundle.ts @@ -0,0 +1,58 @@ +import { readFileSync } from 'node:fs'; + +/** + * Function that uploads a zip bundle to the endpoint. + * @param endpoint string The endpoint to upload to + * @param bundle string The filename to upload + * @returns Object + */ +export const uploadAppBundle = async (endpoint: string, bundle: string) => { + const state: { buffer: Buffer | null } = { + buffer: null, + }; + + try { + state.buffer = readFileSync(bundle); + } catch { + throw new Error(`😞 Could not read bundle ${bundle}, does it exist?`); + } + + const requestBundle = await fetch(endpoint, { + method: 'POST', + body: state.buffer, + headers: { + Authorization: `Bearer ${process.env.FUSION_TOKEN}`, + 'Content-Type': 'application/zip', + }, + }); + + if (requestBundle.status === 401 || requestBundle.status === 403) { + throw new Error( + `This is not allowed for this role on this app. HTTP message: ${requestBundle.statusText}`, + ); + } + + if (requestBundle.status === 404) { + throw new Error(`This app do not exist. HTTP message: ${requestBundle.statusText}`); + } + + if (requestBundle.status === 409) { + throw new Error( + `This version is already published. HTTP message: ${requestBundle.statusText}`, + ); + } + + if (requestBundle.status === 410) { + throw new Error(`This app is deleted. HTTP message: ${requestBundle.statusText}`); + } + + if (!requestBundle.ok) { + const json = await requestBundle.json(); + console.error(json); + throw new Error( + `Failed to publish bundle. HTTP status ${requestBundle.status}, ${requestBundle.statusText}`, + ); + } + + return requestBundle.json(); +}; diff --git a/packages/cli/src/index.tsx b/packages/cli/src/index.tsx deleted file mode 100644 index fee3993772..0000000000 --- a/packages/cli/src/index.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as ReactDOM from 'react-dom/client'; - -// import 'vite/modulepreload-polyfill'; - -const Test = (args: { env: unknown }) => { - return ( -
-

Hello there this is all! good 🤙🏻

-
{JSON.stringify(args, undefined, 4)}
-
- ); -}; - -export const render = (el: HTMLElement, args: { env: unknown }) => { - const root = ReactDOM.createRoot(el); - root.render(); - return () => root.unmount(); -}; - -export default render; diff --git a/packages/cli/src/lib/app-config.ts b/packages/cli/src/lib/app-config.ts index fae8566a98..2eef689e58 100644 --- a/packages/cli/src/lib/app-config.ts +++ b/packages/cli/src/lib/app-config.ts @@ -11,13 +11,7 @@ import { AssertionError, assertObject } from './utils/assert.js'; import { ResolvedAppPackage } from './app-package.js'; import deepmerge from 'deepmerge/index.js'; -// TODO extend defined type in app-package -export type AppConfig = { - /** application config */ - environment?: Record; - /** application urls @todo missing scope, use environment until further notice */ - endpoints?: Record; -}; +import type { AppConfig } from '@equinor/fusion-framework-module-app'; type FindAppConfigOptions = FindConfigOptions & { file?: string; @@ -26,7 +20,7 @@ type FindAppConfigOptions = FindConfigOptions & { export type AppConfigFn = ( env: ConfigExecuterEnv, args: { base: AppConfig }, -) => AppConfig | Promise; +) => AppConfig | Promise | void; export type AppConfigExport = AppConfig | AppConfigFn; export const appConfigFilename = 'app.config'; @@ -78,7 +72,7 @@ export const createAppConfig = async ( ): Promise<{ config: AppConfig; path?: string }> => { const resolved = await resolveAppConfig(options); if (resolved) { - const config = await initiateConfig(resolved.config, env, { base }); + const config = (await initiateConfig(resolved.config, env, { base })) ?? {}; assertAppConfig(config); return { config, path: resolved.path }; } else if (options?.file) { diff --git a/packages/cli/src/lib/app-manifest.ts b/packages/cli/src/lib/app-manifest.ts index 108e8993b1..9871ac8888 100644 --- a/packages/cli/src/lib/app-manifest.ts +++ b/packages/cli/src/lib/app-manifest.ts @@ -1,6 +1,12 @@ -import deepmerge from 'deepmerge'; +import deepMerge from 'deepmerge'; +import { execSync } from 'node:child_process'; -import { ResolvedAppPackage, resolveAppKey, resolveEntryPoint } from './app-package.js'; +import { + AppPackageJson, + resolveAppKey, + ResolvedAppPackage, + resolveEntryPoint, +} from './app-package.js'; import { loadConfig, @@ -13,33 +19,16 @@ import { import { AssertionError, assert, assertObject } from './utils/assert.js'; import { RecursivePartial } from './utils/types.js'; - -export type AppManifest = { - // version: { - // major: number; - // minor: number; - // patch: number; - // }; - version: string; - key: string; - entry: string; - name: string; - shortName: string; - description?: string; - admins?: string[]; - owners?: string[]; - main?: string; - icon?: string; - /** this will be deprecated when new app management is live */ - resource?: string[]; -}; +import { AppManifest } from '@equinor/fusion-framework-module-app'; +import { parse as parseSemver } from 'semver'; +import { StandardIncludeAssetExtensions } from './plugins/app-assets/index.js'; export type AppManifestFn = ( env: ConfigExecuterEnv, args: { base: AppManifest; }, -) => AppManifest | Promise; +) => Partial | Promise | void> | void; type FindManifestOptions = FindConfigOptions & { file?: string; @@ -51,16 +40,11 @@ export const manifestConfigFilename = 'app.manifest.config'; /** * Define a manifest for an application * - * @see {@link mergeManifests | extend manifest} - * * @example * ```ts * export default defineAppManifest(async() => { - * // fetch some data, example owner from secrets - * const owner = gh.secrets.owners: * return { - * ...all_required_attributes - * owners, + * ...AppManifest_properties * } * }) * ``` @@ -70,6 +54,8 @@ export const defineAppManifest = (fn: AppManifestFn) => fn; export function assertAppManifest(value: AppManifest): asserts value { assert(value, 'expected manifest'); + assert(value.build, 'expected build'); + assert(parseSemver(value.build?.version), 'invalid version'); // TODO make assertions } @@ -99,8 +85,8 @@ export const mergeManifests = ( base: RecursivePartial, overrides: RecursivePartial, ): AppManifest => { - const manifest = deepmerge(base, overrides) as unknown as AppManifest; - assertAppManifest(manifest as unknown as AppManifest); + const manifest = deepMerge(base, overrides) as AppManifest; + assertAppManifest(manifest); return manifest; }; @@ -124,7 +110,33 @@ export const resolveManifest = async ( path: options.file, }; } - return resolveConfig(manifestConfigFilename, { find: options }); + return resolveConfig(manifestConfigFilename, { find: options }); +}; + +const resolveGithubRepo = (pkg: AppPackageJson) => { + try { + /* get reporurl from package.json */ + if (pkg.repository) { + return typeof pkg.repository === 'string' ? pkg.repository : pkg.repository.url; + } else { + /* get reporurl from git command */ + return execSync('git remote get-url origin') + .toString() + .trim() + .replace('git@github.com:', 'https://github.com/') + .replace(/.git$/, ''); + } + } catch { + return undefined; + } +}; + +const resolveGitCommitSha = () => { + try { + return execSync('git rev-parse HEAD').toString().trim(); + } catch { + return undefined; + } }; export const createManifestFromPackage = (pkg: ResolvedAppPackage): AppManifest => { @@ -132,20 +144,27 @@ export const createManifestFromPackage = (pkg: ResolvedAppPackage): AppManifest assertObject(packageJson, 'expected packageJson'); assert(packageJson.name, 'expected [name] in packageJson'); assert(packageJson.version, 'expected [version] in packageJson'); - const key = resolveAppKey(pkg.packageJson); - const entry = resolveEntryPoint(pkg.packageJson); - assert(entry, 'expected [version] in packageJson'); - - const manifest = { - key, - entry, - version: packageJson.version, - // TODO manifest should not be loaded from package - name: packageJson.manifest?.name ?? key, - shortName: packageJson.manifest?.shortName ?? key, - description: packageJson.description, - } satisfies AppManifest; - assertAppManifest(manifest); + const entryPoint = resolveEntryPoint(packageJson); + const manifest: AppManifest = { + appKey: resolveAppKey(pkg.packageJson), + displayName: packageJson.name, + description: packageJson.description || '', + keywords: packageJson.keywords, + type: 'standalone', + build: { + entryPoint, + version: packageJson.version, + timestamp: new Date().toISOString(), + githubRepo: resolveGithubRepo(packageJson), + commitSha: resolveGitCommitSha(), + annotations: packageJson.annotations as Record | undefined, + projectPage: packageJson.homepage, + allowedExtensions: StandardIncludeAssetExtensions.map( + // TODO: @jaysencpp, this is just 🫤, extensions should not require leading dot + (ext) => `.${ext}`, + ), + }, + }; return manifest; }; @@ -156,7 +175,8 @@ export const createManifest = async ( ): Promise<{ manifest: AppManifest; path?: string }> => { const resolved = await resolveManifest(options); if (resolved) { - const manifest = await initiateConfig(resolved.config, env, { base }); + const configuredManifest = await initiateConfig(resolved.config, env, { base }); + const manifest = deepMerge(base, configuredManifest ?? {}) as AppManifest; assertAppManifest(manifest); return { manifest, path: resolved.path }; } else if (options?.file) { diff --git a/packages/cli/src/lib/app-package.ts b/packages/cli/src/lib/app-package.ts index 2fb08cab98..93964d75d6 100644 --- a/packages/cli/src/lib/app-package.ts +++ b/packages/cli/src/lib/app-package.ts @@ -1,10 +1,13 @@ +import { existsSync } from 'node:fs'; +import { dirname, relative } from 'node:path'; + import { PackageJson, readPackageUp, type NormalizeOptions as ResolveAppPackageOptions, } from 'read-package-up'; -import { AppManifest } from './app-manifest.js'; +import { AppManifest } from '@equinor/fusion-framework-module-app'; import { assert } from './utils/assert.js'; export type AppPackageJson = PackageJson & { @@ -31,20 +34,66 @@ export function defineAppPackage(fnOrObject: DefinePackageExporter): DefinePacka return fnOrObject; } -export const resolveEntryPoint = (packageJson: Pick) => { - const { main: entry = packageJson.module } = packageJson; - assert(entry, 'expected [main|module] in packageJson'); - return entry; +/** + * Resolves the entry point of a given package. + * + * This function attempts to find the entry point of a package by checking several + * common properties in the package's `package.json` file, such as `entrypoint`, `main`, + * and `module`. If none of these properties are found, it defaults to checking for + * common entry files like `src/index.ts`, `src/index.tsx`, `src/index.js`, and `src/index.jsx`. + * + * @param pkg - The resolved application package containing the package.json and path information. + * @returns The relative path to the resolved entry point. + * @throws Will throw an error if no entry point can be resolved. + */ +export const resolveEntryPoint = (packageJson: PackageJson, pkgPath: string = ''): string => { + const entrypoint = [ + packageJson.entrypoint, + packageJson.main, + packageJson.module, + 'src/index.ts', + 'src/index.tsx', + 'src/index.js', + 'src/index.jsx', + ] + .filter((x): x is string => !!x) + .map((x): string => relative(dirname(pkgPath), x)) + .find((entry) => existsSync(entry)); + + assert(entrypoint, 'failed to resolve entrypoint'); + + return entrypoint; }; +/** + * Resolves the application key from the given package.json object. + * + * @param packageJson - An object containing the 'name' property from the package.json. + * @returns The resolved application key, which is the package name with any leading '@' or scope removed. + * @throws Will throw an error if the 'name' property is not present in the packageJson. + */ export const resolveAppKey = (packageJson: Pick) => { assert(packageJson.name, 'expected [name] in packageJson'); return packageJson.name.replace(/^@|\w.*\//gm, ''); }; -// TODO validate package json -export const assertPackage = (_pkg: Partial) => {}; +/** + * Asserts the validity of a given package by resolving its application key and entry point. + * + * @param pkg - A partial representation of the application's package JSON. + */ +export const assertPackage = (pkg: Partial) => { + assert(resolveAppKey(pkg)); + assert(resolveEntryPoint(pkg as AppPackageJson)); +}; +/** + * Resolves the application package by searching for the nearest `package.json` file. + * + * @param options - Optional parameters to customize the search behavior. + * @returns A promise that resolves to the found package information. + * @throws Will throw an error if the `package.json` file is not found. + */ export const resolveAppPackage = async ( options?: ResolveAppPackageOptions, ): Promise => { diff --git a/packages/cli/src/lib/index.ts b/packages/cli/src/lib/index.ts index 68448c3b0d..d1c6efdfa7 100644 --- a/packages/cli/src/lib/index.ts +++ b/packages/cli/src/lib/index.ts @@ -1,14 +1,8 @@ -export { - mergeManifests, - defineAppManifest, - type AppManifest, - type AppManifestFn, -} from './app-manifest.js'; +export { mergeManifests, defineAppManifest, type AppManifestFn } from './app-manifest.js'; export { mergeAppConfigs, defineAppConfig, - type AppConfig, type AppConfigFn, type AppConfigExport, } from './app-config.js'; diff --git a/packages/cli/src/lib/plugins/app-assets/README.md b/packages/cli/src/lib/plugins/app-assets/README.md new file mode 100644 index 0000000000..1c67b1eb35 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/README.md @@ -0,0 +1,78 @@ +## App Assets Plugin + +The App Assets plugin is a CLI plugin that allows you to manage your app's assets. +This plugin resolves the issue where assets are not extracted from the app's source code since the app is in `lib` mode. + +There are plugins which extract assets from the app's source code, but they will not generate the correct paths for the assets. +secondly, the assets will have a generic `import` which means all assets will be loaded as soon as the app `entrypoint` is loaded. + +> the magic sauce is injecting an `URL` creation with base of the import path of the loaded script. +> ```typescript +> // injected code by plugin +> export default new URL(assetPath, import.meta.url).href +>``` + +### Configuration + +```typescript +export default { + plugins: [ + AppAssetExportPlugin(), + ] +} +``` + +### AssetFilter + +Method to create a filter pattern for the plugin to include or exclude files. + +```typescript +AppAssetExportPlugin( + include: createExtensionFilterPattern([ + 'png', 'jpg', 'jpeg', 'gif', + ]), + exclude: createExtensionFilterPattern([ + 'svg', + ]), +), +``` + +```typescript +AppAssetExportPlugin( + include: createExtensionFilterPattern( + manifest.build.allowedExtensions + ), +), +``` + +### Options + +#### name +Output name of the resource file, its usage aligns with the name option of the file-loader. + +```typescript +AppAssetExportPlugin({ + // default: '[name].[ext]' + name: '[name].[contenthash:8].[ext]?[query]' +}) +``` + +#### include +A valid [picomatch](https://github.com/micromatch/picomatch#globbing-features) pattern, or array of patterns indicate which files need to be handled by the plugin. + +```typescript +AppAssetExportPlugin({ + // default: see static file extensions + include: /\.a?png(\?.*)?$/ +}) +``` + +#### exclude +A valid [picomatch](https://github.com/micromatch/picomatch#globbing-features) pattern, or array of patterns indicate which files need to be handled by the plugin. + +```typescript +AppAssetExportPlugin({ + // default: undefined + exclude: /\.a?svg(\?.*)?$/ +}) +``` \ No newline at end of file diff --git a/packages/cli/src/lib/plugins/app-assets/app-asset-plugin.ts b/packages/cli/src/lib/plugins/app-assets/app-asset-plugin.ts new file mode 100644 index 0000000000..4474ea4e82 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/app-asset-plugin.ts @@ -0,0 +1,112 @@ +import { createFilter, type ResolvedConfig, type FilterPattern, type Plugin } from 'vite'; + +import { emitAssetSync } from './emit-asset.js'; +import { resolveAssetId } from './resolve-asset-id.js'; +import { createExtensionFilterPattern } from './extension-filter-pattern.js'; + +import { ASSET_EXTENSIONS, PLUGIN_NAME } from './static.js'; + +const defaultInclude = createExtensionFilterPattern(ASSET_EXTENSIONS); + +/** + * A Vite plugin to handle external resources in a library build. + * + * @param options - Configuration options for the plugin. + * @param options.name - Optional name for the emitted assets. + * @param options.include - Filter pattern to include specific files. + * @param options.exclude - Filter pattern to exclude specific files. + * @returns A Vite plugin object. + * + * @remarks + * This plugin is intended to be used only during the Vite library build process. + * It resolves asset IDs, emits assets, and exports them as URLs. + * + * @example + * ```typescript + * // vite.config.ts + * import { ExternalAppAssetPlugin } from '@equinor/fusion-framework-cli/plugins/app-assets-plugin'; + * + * export default { + * plugins: [ + * ExternalAppAssetPlugin({ + * name: 'my-asset', + * include: ['svg', 'png'], + * exclude: 'node_modules/**', + * }), + * ], + * }; + * ``` + */ +export const AppAssetExportPlugin = ( + options: { + name?: string; + include?: FilterPattern; + exclude?: FilterPattern; + } = {}, +): Plugin => { + const { name, include = defaultInclude, exclude } = options; + + let viteConfig: ResolvedConfig; + + const assetsPathMap = new Map(); + + const filter = createFilter(include, exclude); + + return { + name: PLUGIN_NAME, + enforce: 'pre', + apply: 'build', + configResolved(config) { + viteConfig = config; + }, + async resolveId(source, importer = '', opts) { + if (viteConfig.build.lib === false) { + this.warn(`this plugin is only for vite build lib`); + } + // skip resolves triggered by plugin self + if (opts.custom?.caller === PLUGIN_NAME) { + return null; + } + + // resolve asset ID, the ID should refer to the actual asset file + const assetId = await resolveAssetId(this, source, importer, { + ...opts, + custom: { + ...opts.custom, + caller: PLUGIN_NAME, + }, + }); + + // skip if asset is not found or filtered out + const { id } = assetId ?? {}; + const shouldIncludeAsset = id && filter(id); + if (!shouldIncludeAsset) { + return null; + } + + try { + // emit asset and index the asset path + const { outDir, assetsDir } = viteConfig.build; + const assetPath = emitAssetSync(this, id, { + name, + outDir, + assetsDir, + }); + assetsPathMap.set(id, assetPath!); + } catch (err) { + this.warn((err as Error).message); + return null; + } + }, + load(id) { + // lookup asset path and export as URL + const assetPath = assetsPathMap.get(id); + if (assetPath) { + // ensure asset path is relative from the script load path + return `export default new URL(/* @vite-ignore */'${assetPath}', import.meta.url).href`; + } + }, + }; +}; + +export default AppAssetExportPlugin; diff --git a/packages/cli/src/lib/plugins/app-assets/emit-asset.ts b/packages/cli/src/lib/plugins/app-assets/emit-asset.ts new file mode 100644 index 0000000000..a8b15cfb02 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/emit-asset.ts @@ -0,0 +1,64 @@ +import path from 'node:path'; + +import { type PluginContext } from 'rollup'; +import { interpolateName } from 'loader-utils'; + +import { readAssetContentSync } from './read-asset-content.js'; + +type LoaderContext = Parameters[0]; + +/** + * Synchronously emits an asset file based on the provided context and options. + * + * @param context - The plugin context used for emitting the file and logging warnings. + * @param id - The identifier of the asset, which may include a resource query. + * @param options - Optional parameters for customizing the emitted asset. + * @param options.name - The name template for the emitted asset file. Defaults to '[name].[ext]'. + * @param options.outDir - The output directory where the asset will be emitted. Defaults to 'dist'. + * @param options.assetsDir - The directory within the output directory where assets will be stored. Defaults to 'assets'. + * @returns The path of the emitted asset relative to the assets directory, or null if the asset content could not be read. + */ +export const emitAssetSync = ( + context: PluginContext, + id: string, + options: { + name?: string; + outDir?: string; + assetsDir?: string; + } = {}, +): string | null => { + const { outDir = 'dist', assetsDir = 'assets', name = '[name].[ext]' } = options; + const [originalFileName, resourceQuery] = id.split('?'); + + // read asset content, early return if not found + const content = readAssetContentSync(id); + if (!content || content.byteLength === 0) { + throw new Error(`Could not read asset content for ${id}`); + } + + // generate asset file name + const url = interpolateName( + { + resourcePath: originalFileName, + resourceQuery, + } as LoaderContext, + name, + { content }, + ); + + const assetPath = path.posix.join(assetsDir, url); + const fileName = assetPath.replace(`?${resourceQuery}`, ''); + const fullName = path.join(path.isAbsolute(outDir) ? process.cwd() : '', outDir, assetPath); + + // write asset to file + context.emitFile({ + fileName, + name: fullName, + type: 'asset', + source: content, + }); + + return assetPath; +}; + +export default emitAssetSync; diff --git a/packages/cli/src/lib/plugins/app-assets/extension-filter-pattern.ts b/packages/cli/src/lib/plugins/app-assets/extension-filter-pattern.ts new file mode 100644 index 0000000000..ff84740f87 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/extension-filter-pattern.ts @@ -0,0 +1,22 @@ +/** + * Removes the leading dot from a file extension string. + * `.` are a RegExp special character, so it needs to be escaped. + */ +const trimLeadingDot = (ext: string) => ext.replace(/^\./, ''); + +/** + * Creates a regular expression pattern to filter files based on their extensions. + * + * @param exts - An array of file extensions to include in the pattern. + * @returns A RegExp object that matches files with the specified extensions. + * + * @example + * ```typescript + * const pattern = createExtensionFilterPattern(['.js', '.ts']); + * console.log(pattern); // Output: /\.(js|ts)(\?.*)?$/ + * ``` + */ +export const createExtensionFilterPattern = (exts: string[]) => + new RegExp(`\\.(${exts.map(trimLeadingDot).join('|')})(\\?.*)?$`); + +export default createExtensionFilterPattern; diff --git a/packages/cli/src/lib/plugins/app-assets/index.ts b/packages/cli/src/lib/plugins/app-assets/index.ts new file mode 100644 index 0000000000..64962de6b4 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/index.ts @@ -0,0 +1,4 @@ +export { default, AppAssetExportPlugin } from './app-asset-plugin.js'; +export { createExtensionFilterPattern } from './extension-filter-pattern.js'; + +export { ASSET_EXTENSIONS as StandardIncludeAssetExtensions } from './static.js'; diff --git a/packages/cli/src/lib/plugins/app-assets/read-asset-content.ts b/packages/cli/src/lib/plugins/app-assets/read-asset-content.ts new file mode 100644 index 0000000000..d1f22ea5e1 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/read-asset-content.ts @@ -0,0 +1,39 @@ +import { existsSync, readFileSync } from 'node:fs'; + +/** + * Cache for the content of assets. + */ +const assetsContentMap = new Map(); + +/** + * Reads the content of an asset synchronously. + * + * @param id - The identifier of the asset, which can include a query string. + * @returns The content of the asset as a Buffer if it exists, otherwise null. + * + * @remarks + * - If the asset content is already cached, it returns the cached content. + * - The function extracts the filename from the identifier by removing any query string. + * - If the file exists, it reads the content, caches it, and then returns it. + * - If the file does not exist, it logs a warning and returns null. + */ +export const readAssetContentSync = (id: string): Buffer | null => { + // check if the asset is already loaded + if (assetsContentMap.has(id)) { + return assetsContentMap.get(id)!; + } + + // extract the filename without query + const [pureId] = id.split('?'); + + // if the file exists, read, cache and return content + if (existsSync(pureId)) { + const content = readFileSync(pureId); + assetsContentMap.set(id, content); + return content; + } + + return null; +}; + +export default readAssetContentSync; diff --git a/packages/cli/src/lib/plugins/app-assets/resolve-asset-id.ts b/packages/cli/src/lib/plugins/app-assets/resolve-asset-id.ts new file mode 100644 index 0000000000..e6929be2fc --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/resolve-asset-id.ts @@ -0,0 +1,62 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +import { type PluginContext, type PartialResolvedId } from 'rollup'; + +import { PLUGIN_NAME } from './static.js'; + +/** + * Try to resolve the assets directly based on the ID and importer. + */ +const localResolve = (id: string, importer: string): PartialResolvedId | null => { + if (path.isAbsolute(id)) { + return { id, external: 'absolute', resolvedBy: PLUGIN_NAME }; + } else if (id.startsWith('.')) { + return { + id: path.resolve(path.dirname(importer), id), + external: 'relative', + resolvedBy: PLUGIN_NAME, + }; + } + return null; +}; + +/** + * Resolves the asset ID based on the provided context, ID, importer, and options. + * + * @param context - The plugin context used for resolving the ID. + * @param id - The asset ID to resolve. + * @param importer - The path of the module that is importing the asset. + * @param options - Optional resolution options. + * @returns A promise that resolves to a `PartialResolvedId` object or `null`. + * + * The function handles three cases: + * 1. If the ID is an absolute path, it returns an object with the ID, marked as external and resolved by the plugin. + * 2. If the ID is a relative path, it resolves the path relative to the importer and returns an object with the resolved ID, marked as external and resolved by the plugin. + * 3. For all other cases, it delegates the resolution to the context's resolve method. + */ +export const resolveAssetId = async ( + context: PluginContext, + id: string, + importer: string, + options?: Parameters[2], +): Promise => { + const resolve = localResolve(id, importer); + /** + * Check if the asset can be resolved locally. + * If the asset is found, return the resolved asset. + * + * @remarks + * The Id alone might not be enough to resolve the asset. + * Rollup only gives the ID as the import from the source. + * + * For example, if the import is `import svgData from './assets/image.svg';`, + * the actual file is `./assets/image.svg.js`. + * + * In this we try to as the context to resolve the ID. + */ + if (resolve && fs.existsSync(resolve.id)) { + return resolve; + } + return await context.resolve(id, importer, options); +}; diff --git a/packages/cli/src/lib/plugins/app-assets/static.ts b/packages/cli/src/lib/plugins/app-assets/static.ts new file mode 100644 index 0000000000..5b0c75bbb6 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-assets/static.ts @@ -0,0 +1,15 @@ +export const PLUGIN_NAME = 'vite:fusion:app-assets'; + +/** + * General asset extensions. + */ +export const ASSET_EXTENSIONS = [ + // Images + ...['png', 'jpg', 'jpeg', 'gif', 'svg', 'ico', 'webp'], + // Videos and audio + ...['mp4', 'webm', 'mp3'], + // Fonts + ...['woff2', 'woff', 'eot', 'ttf', 'otf'], + // Documents + ...['pdf', 'md', 'txt'], +]; diff --git a/packages/cli/src/lib/plugins/app-proxy/README.md b/packages/cli/src/lib/plugins/app-proxy/README.md new file mode 100644 index 0000000000..00dea11fdc --- /dev/null +++ b/packages/cli/src/lib/plugins/app-proxy/README.md @@ -0,0 +1,77 @@ +## App Proxy Plugin + +The `App Proxy Plugin` is a Vite plugin that allows you to proxy requests to the Fusion App Service. This plugin is essential when developing applications that require assets from the Fusion App Service. It intercepts requests to the Fusion App Service and injects the necessary headers to authenticate the request. + +Additionally, this plugin intercepts requests to the Fusion App Service to generate local app manifest and configuration files. It also resolves SPA routing to the local source code. + +> [!WARNING] +> The `App Proxy Plugin` is only available in development mode. +> It is not recommended to use this plugin in production. +> The plugin will cache the user authentication token in the node process memory. This is necessary to authenticate asset requests to the Fusion App Service, since there is no valid cookie for the development domain. + +### Configuration + +The `App Proxy Plugin` can be configured by passing an object to the plugin. The following options are available: + +**proxy** + +- `target` - The target URL of the Fusion App Service. This is required. +- `path`- url path to intercept and proxy to the Fusion App Service. +- `onProxyReq` - A function that will be called before the request is sent to the Fusion App Service. This is optional. + +**app** _[optiontional]_ + +> [!NOTE] +> When no app configuration is provided, all request on `path` will be intercepted and proxy to the Fusion App Service. + +- `key`: The app key of the application. +- `version`: The build version of the application. +- `generateManifest` - A callback function that will be called to generate the app manifest. +- `generateConfig` - A callback function that will be called to generate the app configuration. + +### Example + +```typescript +export default defineConfig({ + plugins: [ + appProxyPlugin({ + proxy: { + path: '/app-proxy', + target: 'https://fusion-s-apps-ci.azurewebsites.net/', + onProxyReq: (proxyReq, req, res) => { + proxyReq.on('response', (res) => { console.log(res.statusCode) }); + }, + }, + app: { + key: 'my-app', + version: '1.0.0', + generateConfig: async () => ({}), + generateManifest: async () => ({}), + }, + }) + ] +}) +``` + +**Example API Requests** +```typescript +// will generate app config by provided function +fetch('/app-proxy/apps/my-app/builds/1.0.0/config'); + +// will proxy to the target, since version does not match +fetch('/app-proxy/apps/my-app/builds/0.0.9/config'); + +// will proxy to the target, since app key does not match +fetch('/app-proxy/apps/other-app/builds/1.0.0/config'); +``` + +**Example Asset Requests** +```typescript +// will generate bundle by provided function +fetch('/app-proxy/bundles/my-app/builds/1.0.0/index.js'); + +// will proxy to the target, since version does not match +fetch('/app-proxy/bundles/my-app/builds/0.0.9/index.js'); +``` + + diff --git a/packages/cli/src/lib/plugins/app-proxy/app-proxy-plugin.ts b/packages/cli/src/lib/plugins/app-proxy/app-proxy-plugin.ts new file mode 100644 index 0000000000..2c7762b627 --- /dev/null +++ b/packages/cli/src/lib/plugins/app-proxy/app-proxy-plugin.ts @@ -0,0 +1,155 @@ +import { Plugin } from 'vite'; + +import { AppConfig, AppManifest } from '@equinor/fusion-framework-app'; +import { ClientRequest, IncomingMessage, ServerResponse } from 'node:http'; + +/** + * Preserve token for executing proxy assets + * + * @remarks + * This assumes the client will execute a api call using bearer token before + * acquiring a asset. By default the Framework will execute a rest call to load + * application manifest for resolving build assets to import. + * + * @remarks + * This is a quick and dirty method to authorize requests without bearer token + * like browser `import`. + * The correct way would be to have a auth controller within the dev-server, + * but since the token is only exposed to the plugin and the cli is a tool for local + * development, this should be sufficient. + */ +let __APP_API_TOKEN__ = ''; + +/** + * Options for configuring the App Proxy Plugin. + * + * @remarks + * When not providing an app configuration, the plugin will only proxy requests to the target. + */ +export type AppProxyPluginOptions = { + /** Configuration for the proxy. */ + proxy: { + /** The path to be proxied. */ + path: string; + /** The target URL for the proxy. */ + target: string; + /** Optional callback function to modify the proxy request. */ + onProxyReq?: (proxyReq: ClientRequest, req: IncomingMessage, res: ServerResponse) => void; + }; + /** Optional configuration for the app. */ + app?: { + /** application key */ + key: string; + /** application version */ + version: string; + /** callback function for generating configuration for the application */ + generateConfig: () => Promise; + /** callback function for generating manifest for the application */ + generateManifest: () => Promise; + }; +}; + +/** + * The `appProxyPlugin` function creates a Vite plugin that configures a proxy for API and bundle requests + * to the Fusion apps backend. It also serves the app manifest, config, and local bundles if an app is provided. + * + * @param {AppProxyPluginOptions} options - The options for configuring the app proxy plugin. + * + * @returns {Plugin} - The configured Vite plugin. + * + * @example + * ```typescript + * const plugin = appProxyPlugin({ + * proxy: { + * path: '/app-proxy', + * target: 'https://fusion-s-apps-ci.azurewebsites.net/', + * onProxyReq: (proxyReq, req, res) => { + * proxyReq.on('response', (res) => { console.log(res.statusCode) }); + * }, + * }, + * app: { + * key: 'my-app', + * version: '1.0.0', + * generateConfig: async () => ({}), + * generateManifest: async () => ({}), + * }, + * }); + * + * // api calls + * fetch('/app-proxy/apps/my-app/builds/1.0.0/config'); // will generate app config by provided function + * fetch('/app-proxy/apps/my-app/builds/0.0.9/config'); // will proxy to the target, since version does not match + * fetch('/app-proxy/apps/other-app/builds/1.0.0/config'); // will proxy to the target, since app key does not match + * + * // asset calls + * fetch('/app-proxy/bundles/my-app/builds/1.0.0/index.js'); // will generate bundle by provided function + * fetch('/app-proxy/bundles/my-app/builds/0.0.9/index.js'); // will proxy to the target, since version does not match + * ``` + * + */ +export const appProxyPlugin = (options: AppProxyPluginOptions): Plugin => { + const { + proxy: { onProxyReq = () => void 0, path: proxyPath, target }, + } = options; + return { + name: 'fusion:app-proxy', + apply: 'serve', + config(config) { + config.server ??= {}; + config.server.proxy = { + // proxy all api calls to the fusion apps backend + [proxyPath]: { + target, + changeOrigin: true, + secure: false, + rewrite: (path) => path.replace(proxyPath, ''), + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + const token = proxyReq.getHeader('authorization'); + if (typeof token === 'string') { + // preserve token for executing proxy assets + __APP_API_TOKEN__ = token; + } else if (__APP_API_TOKEN__) { + // apply token to proxy request + proxyReq.setHeader('authorization', __APP_API_TOKEN__); + } + }); + proxy.on('proxyReq', onProxyReq); + }, + }, + }; + }, + configureServer(server) { + const { app } = options; + + // disable local assets if no app configuration provided + if (!app) return; + + // serve app manifest if request matches the current app + // todo this should have version + const manifestPath = `${proxyPath}/apps/${app.key}`; + server.middlewares.use(manifestPath, async (_req, res) => { + res.setHeader('content-type', 'application/json'); + res.end(JSON.stringify(await app.generateManifest())); + }); + + // serve app config if request matches the current app and version + const configPath = `${proxyPath}/apps/${app.key}/builds/${app.version}/config`; + server.middlewares.use(configPath, async (_req, res) => { + res.setHeader('content-type', 'application/json'); + res.end(JSON.stringify(await app.generateConfig())); + }); + + // serve local bundles if request matches the current app and version + const bundlePath = `${proxyPath}/bundles/apps/${app.key}/${app.version}`; + server.middlewares.use(async (req, _res, next) => { + if (req.url?.match(bundlePath)) { + // remove proxy path from url + req.url = req.url!.replace(bundlePath, ''); + } + next(); + }); + }, + }; +}; + +export default appProxyPlugin; diff --git a/packages/cli/src/lib/plugins/app-proxy/index.ts b/packages/cli/src/lib/plugins/app-proxy/index.ts new file mode 100644 index 0000000000..b3885ac02d --- /dev/null +++ b/packages/cli/src/lib/plugins/app-proxy/index.ts @@ -0,0 +1 @@ +export { default, appProxyPlugin, type AppProxyPluginOptions } from './app-proxy-plugin.js'; diff --git a/packages/cli/src/lib/plugins/external-public/README.md b/packages/cli/src/lib/plugins/external-public/README.md new file mode 100644 index 0000000000..91563469c8 --- /dev/null +++ b/packages/cli/src/lib/plugins/external-public/README.md @@ -0,0 +1,40 @@ +## External Public Plugin +This plugin is useful for serving a static site from a directory different from where the Vite server is running. + +Vite's built-in `mode: 'spa'` only looks for the `index.html` file in the configured `root` directory. Therefore, this plugin is necessary to serve the `index.html` file from an alternative directory. + + +### How it works + +The plugin will intercept requests to the Vite server and serve the static files from the specified directory. If the requested file is not found in the specified directory, the request will be passed to the Vite server to handle. + +> [!NOTE] +> The middleware will first check if a file by the same name exists in the runtime root directory. This is to prevent hijacking requests to the Vite dev server. +> example: `/asset/cute-cat.jpg` can be both in the `publicDir` and the external directory. The middleware will first check the `publicDir` and `root` before checking the external directory. +> +> **A good practice is to use content hash on external assets to prevent conflict** + +The plugin will serve the `index.html` file from the specified directory when the requested file is not found. This is required for single-page applications (SPAs) that use client-side routing. + + +### Configuration + +The `External Public Plugin` can be configured by passing an object to the plugin. The following options are available: + +- `path` - The path to static files that should be served by the Vite server. This is required. + +```typescript +export default defineConfig({ + plugins: [ + externalPublicPlugin('./my-static-site/demo-portal') + ] +}); +``` + +**Filtering** + +```typescript +externalPublicPlugin('./my-static-site/demo-portal', { + exclude: /\.(ts|tsx)$/ +}) +``` diff --git a/packages/cli/src/lib/plugins/external-public/external-public-plugin.ts b/packages/cli/src/lib/plugins/external-public/external-public-plugin.ts new file mode 100644 index 0000000000..9eca453255 --- /dev/null +++ b/packages/cli/src/lib/plugins/external-public/external-public-plugin.ts @@ -0,0 +1,123 @@ +import { join } from 'node:path'; +import { readFileSync, existsSync } from 'node:fs'; + +import mime from 'mime'; + +import { createFilter, type FilterPattern, type ResolvedConfig, type Plugin } from 'vite'; + +/** + * Creates a plugin that serves an external public directory. + * + * This plugin is useful when you want to serve a static site from a different directory than the one where the Vite server is running. + * Vite`s built in `mode: 'spa'` will only look for the `index.html` file in the configured `root` directory, + * so this plugin is necessary to serve the `index.html` file from a different directory. + * + * @param path - The path to the external public directory. + * @param options - Optional filter patterns to include or exclude specific assets. + * @param options.include - A filter pattern to include specific assets. + * @param options.exclude - A filter pattern to exclude specific assets. + * @returns A Vite plugin object. + * + * The plugin: + * - Sets the `path` configuration to the provided path. + * - Adds a middleware to the server that serves static assets from the specified path. + * - Adds a middleware to the server that serves the `index.html` file from the specified path. + * + * The middleware: + * - Checks if the request is for a static asset and serves it from the specified path. + * - Reads the `index.html` file from the specified path. + * - Transforms the HTML using the server's `transformIndexHtml` method. + * - Responds with the transformed HTML, setting appropriate headers. + */ +export const externalPublicPlugin = ( + path: string, + options?: { + include?: FilterPattern; + exclude?: FilterPattern; + }, +): Plugin => { + let viteConfig: ResolvedConfig; + + const assetFilter = createFilter(options?.include, options?.exclude); + + return { + name: 'fusion:external-public', + apply: 'serve', + configResolved(config) { + viteConfig = config; + }, + configureServer(server) { + // serve the static assets from the provided path + server.middlewares.use(async (req, res, next) => { + const [urlPath] = req.url!.split('?'); + + const assetPath = join(path, urlPath); + + if ( + !assetFilter(assetPath) || + // skip if the request is for index.html + req.url?.match('index.html') || + // skip if request is for a source file + existsSync(join(viteConfig.root, urlPath)) || + // skip if asset is in publicDir + existsSync(join(viteConfig.publicDir, urlPath)) || + // skip if asset does not exist + !existsSync(assetPath) + ) { + return next(); + } + + try { + const content = readFileSync(assetPath); + const contentType = mime.getType(assetPath) || 'application/octet-stream'; + res.writeHead(200, { + 'content-type': contentType, + 'content-length': Buffer.byteLength(content), + 'cache-control': 'no-cache', + ...server.config.server.headers, + }); + res.end(content); + } catch (e) { + next(e); + } + }); + + // intercept requests to serve the index.html file + server.middlewares.use(async (req, res, next) => { + if ( + // Only accept GET or HEAD + (req.method !== 'GET' && req.method !== 'HEAD') || + // Only accept text/html + !req.headers.accept?.includes('text/html') + ) { + return next(); + } + try { + // load the raw html from provided path + const htmlRaw = readFileSync(join(path, 'index.html'), 'utf-8'); + // transform the html, this is where vite plugin hooks are applied + const html = await server.transformIndexHtml( + req.url!, + htmlRaw, + req.originalUrl, + ); + + // apply content headers and configured additional headers + res.writeHead(200, { + 'content-type': 'text/html', + 'content-length': Buffer.byteLength(html), + 'cache-control': 'no-cache', + ...server.config.server.headers, + }); + + // send the transformed html and end the response + res.end(html); + } catch (e) { + next(e); + } + }); + }, + }; +}; + +export default externalPublicPlugin; diff --git a/packages/cli/src/lib/plugins/external-public/index.ts b/packages/cli/src/lib/plugins/external-public/index.ts new file mode 100644 index 0000000000..82c92871aa --- /dev/null +++ b/packages/cli/src/lib/plugins/external-public/index.ts @@ -0,0 +1 @@ +export { default, externalPublicPlugin } from './external-public-plugin.js'; diff --git a/packages/cli/src/lib/utils/config.ts b/packages/cli/src/lib/utils/config.ts index b1dac412ac..59541c3f00 100644 --- a/packages/cli/src/lib/utils/config.ts +++ b/packages/cli/src/lib/utils/config.ts @@ -108,13 +108,15 @@ export const loadConfig = async (file: string): Promise JSON.parse(await readFile(file, 'utf-8')); } + default: + throw Error('unsupported file type'); } }; export function initiateConfig( config: TConfig, ...args: Parameters -): Promise> { +): Promise | void> { return Promise.resolve(config(...(args as ConfigExecuterArgs))); } diff --git a/packages/cli/src/lib/vite-config.ts b/packages/cli/src/lib/vite-config.ts index 66561cf9ae..694281b2e0 100644 --- a/packages/cli/src/lib/vite-config.ts +++ b/packages/cli/src/lib/vite-config.ts @@ -50,10 +50,10 @@ export const createAppViteConfig = async ( options?: FindConfigOptions & { file?: string; }, -): Promise<{ config: UserConfig; path?: string } | void> => { +): Promise<{ config?: UserConfig; path?: string } | void> => { const resolved = await resolveViteConfig(options); if (resolved) { - const config = await initiateConfig(resolved.config, env); + const config = (await initiateConfig(resolved.config, env)) ?? {}; return { config, path: resolved.path }; } else if (options?.file) { throw new AssertionError({ @@ -91,10 +91,8 @@ export const createViteConfig = async ( (process.env.FUSION_LOG_LEVEL ?? env.mode === 'development') ? '3' : '1', }), ], + mode: env.mode, root, - server: { - middlewareMode: true, - }, appType: 'custom', build: { lib: { diff --git a/packages/modules/app/package.json b/packages/modules/app/package.json index 63b728c26f..41be89677e 100644 --- a/packages/modules/app/package.json +++ b/packages/modules/app/package.json @@ -18,6 +18,9 @@ }, "./app/*.js": { "import": "./dist/esm/app/*.js" + }, + "./application.schema.js": { + "import": "./dist/esm/application.schema.js" } }, "types": "dist/types/index.d.ts", @@ -53,7 +56,8 @@ "@equinor/fusion-observable": "workspace:^", "@equinor/fusion-query": "workspace:^", "immer": "^9.0.16", - "rxjs": "^7.8.1" + "rxjs": "^7.8.1", + "zod": "^3.23.8" }, "devDependencies": { "@equinor/fusion-framework-module": "workspace:^", diff --git a/packages/modules/app/src/AppClient.ts b/packages/modules/app/src/AppClient.ts new file mode 100644 index 0000000000..24ecfbbca0 --- /dev/null +++ b/packages/modules/app/src/AppClient.ts @@ -0,0 +1,167 @@ +import { catchError, map, Observable, ObservableInput } from 'rxjs'; + +import { Query } from '@equinor/fusion-query'; +import { queryValue } from '@equinor/fusion-query/operators'; + +import { HttpResponseError, IHttpClient } from '@equinor/fusion-framework-module-http'; +import { jsonSelector } from '@equinor/fusion-framework-module-http/selectors'; + +import { ApiApplicationSchema } from './application.schema'; + +import { AppConfig, AppManifest } from './types'; +import { AppConfigError, AppManifestError } from './errors'; + +export interface IAppClient extends Disposable { + /** + * Fetch app manifest by appKey + */ + getAppManifest: (args: { appKey: string }) => ObservableInput; + + /** + * Fetch all app manifests + */ + getAppManifests: (args?: { filterByCurrentUser?: boolean }) => ObservableInput; + + /** + * Fetch app config by appKey and tag + */ + getAppConfig: (args: { + appKey: string; + tag?: string; + }) => ObservableInput>; +} + +/** + * Transforms an ApiApplicationSchema object into an AppManifest object. + * + * @returns An object conforming to the AppManifest interface. + */ +const ApplicationSchema = ApiApplicationSchema.transform((x): AppManifest => { + const { category, ...props } = x; + return { + ...props, + // TODO: remove deprecated appKey + get key() { + return props.appKey; + }, + // TODO: remove deprecated name + get name() { + return props.displayName; + }, + categoryId: category?.id, + category, + } as AppManifest; +}); + +/** + * The `AppClient` class implements the `IAppClient` interface and provides methods to query + * application manifests and configurations from a backend service. + */ +export class AppClient implements IAppClient { + #manifest: Query; + #manifests: Query; + #config: Query; + + constructor(client: IHttpClient) { + const expire = 1 * 60 * 1000; + this.#manifest = new Query({ + client: { + fn: ({ appKey }) => { + return client.json(`/apps/${appKey}`, { + headers: { + 'Api-Version': '1.0', + }, + selector: async (res: Response) => + ApplicationSchema.parse(await jsonSelector(res)), + }); + }, + }, + key: ({ appKey }) => appKey, + expire, + }); + + this.#manifests = new Query({ + client: { + fn: (filter) => { + const path = filter?.filterByCurrentUser ? '/persons/me/apps' : '/apps'; + return client.json(path, { + headers: { + 'Api-Version': '1.0', + }, + selector: async (res: Response) => { + const response = (await jsonSelector(res)) as { value: unknown[] }; + return ApplicationSchema.array().parse(response.value); + }, + }); + }, + }, + key: (filter) => (filter?.filterByCurrentUser ? 'currentUser' : 'all'), + expire, + }); + + this.#config = new Query({ + client: { + fn: ({ appKey, tag = 'latest' }) => { + return client.json(`/apps/${appKey}/builds/${tag}/config`, { + headers: { + 'Api-Version': '1.0', + }, + }); + }, + }, + key: (args) => JSON.stringify(args), + expire, + }); + } + getAppManifest(args: { appKey: string }): Observable { + return this.#manifest.query(args).pipe( + queryValue, + catchError((err) => { + /** extract cause, since error will be a `QueryError` */ + const { cause } = err; + if (cause instanceof AppManifestError) { + throw cause; + } + if (cause instanceof HttpResponseError) { + throw AppManifestError.fromHttpResponse(cause.response, { cause }); + } + throw new AppManifestError('unknown', 'failed to load manifest', { cause }); + }), + ); + } + + getAppManifests( + args: { filterByCurrentUser?: boolean } | undefined, + ): Observable { + return this.#manifests.query(args).pipe(queryValue); + } + + getAppConfig(args: { + appKey: string; + tag?: string; + }): Observable> { + return this.#config.query(args).pipe( + map((res) => res.value as AppConfig), + catchError((err) => { + /** extract cause, since error will be a `QueryError` */ + const { cause } = err; + if (cause instanceof AppConfigError) { + throw cause; + } + if (cause instanceof HttpResponseError) { + throw AppConfigError.fromHttpResponse(cause.response, { cause }); + } + throw new AppConfigError('unknown', 'failed to load config', { cause }); + }), + ); + } + + [Symbol.dispose]() { + console.warn('AppClient disposed'); + this.#manifest.complete(); + this.#manifests.complete(); + this.#config.complete(); + } +} + +export default AppClient; diff --git a/packages/modules/app/src/AppConfigBuilder.ts b/packages/modules/app/src/AppConfigBuilder.ts deleted file mode 100644 index 338fc651d2..0000000000 --- a/packages/modules/app/src/AppConfigBuilder.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ModuleInitializerArgs, Modules, ModuleType } from '@equinor/fusion-framework-module'; -import { QueryFn, QueryCtorOptions } from '@equinor/fusion-query'; - -import { AppModuleConfig, IAppConfigurator } from './AppConfigurator'; - -import type { AppConfig, AppManifest, ModuleDeps } from './types'; - -export type AppConfigBuilderCallback = ( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - builder: AppConfigBuilder>, -) => void | Promise; - -export class AppConfigBuilder< - // eslint-disable-next-line @typescript-eslint/no-explicit-any - TInit extends ModuleInitializerArgs = ModuleInitializerArgs< - IAppConfigurator, - ModuleDeps - >, -> { - #init: TInit; - constructor( - init: TInit, - public config: Partial = {}, - ) { - this.#init = init; - } - - requireInstance>( - module: TKey, - ): Promise>; - - requireInstance(module: string): Promise; - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - requireInstance(module: string): Promise { - return this.#init.requireInstance(module); - } - - setAppClient( - client: { - getAppManifest: - | QueryFn - | QueryCtorOptions; - getAppManifests: QueryFn | QueryCtorOptions; - getAppConfig: - | QueryFn - | QueryCtorOptions; - }, - expire = 1 * 60 * 1000, - ) { - client; - expire; - - this.config.client = { - getAppManifest: - typeof client.getAppManifest === 'function' - ? { - key: ({ appKey }) => appKey, - client: { - fn: client.getAppManifest, - }, - expire, - } - : client.getAppManifest, - getAppManifests: - typeof client.getAppManifests === 'function' - ? { - // TODO - might cast to checksum - key: () => 'app_manifests', - client: { - fn: client.getAppManifests, - }, - expire, - } - : client.getAppManifests, - getAppConfig: - typeof client.getAppConfig === 'function' - ? { - // TODO - might cast to checksum - key: (args) => JSON.stringify(args), - client: { - fn: client.getAppConfig, - }, - expire, - } - : client.getAppConfig, - }; - } -} diff --git a/packages/modules/app/src/AppConfigurator.ts b/packages/modules/app/src/AppConfigurator.ts index 69b433699f..f1ccebafa4 100644 --- a/packages/modules/app/src/AppConfigurator.ts +++ b/packages/modules/app/src/AppConfigurator.ts @@ -1,44 +1,36 @@ -import type { ModuleInitializerArgs } from '@equinor/fusion-framework-module'; +import { + BaseConfigBuilder, + ConfigBuilderCallback, + type ModuleInitializerArgs, +} from '@equinor/fusion-framework-module'; import type { HttpModule, IHttpClient } from '@equinor/fusion-framework-module-http'; import type { ServiceDiscoveryModule } from '@equinor/fusion-framework-module-service-discovery'; -import type { QueryCtorOptions } from '@equinor/fusion-query'; - -import { AppConfigBuilder, AppConfigBuilderCallback } from './AppConfigBuilder'; import { moduleKey } from './module'; -import type { AppConfig, AppManifest } from './types'; +import AppClient, { IAppClient } from './AppClient'; export interface AppModuleConfig { - proxy: { - path: string; - }; - client: { - getAppManifest: QueryCtorOptions; - getAppManifests: QueryCtorOptions; - getAppConfig: QueryCtorOptions; - }; + client: IAppClient; + // uri which to fetch the assets from aka the bundle of the application + assetUri?: string; } export interface IAppConfigurator { - addConfigBuilder: (init: AppConfigBuilderCallback) => void; + setClient: ( + client_or_cb: + | Promise + | ConfigBuilderCallback, + ) => void; + setAssetUri: (base_or_cb: string | ConfigBuilderCallback) => void; } -export class AppConfigurator implements IAppConfigurator { +export class AppConfigurator + extends BaseConfigBuilder + implements IAppConfigurator +{ defaultExpireTime = 1 * 60 * 1000; - #configBuilders: Array = []; - - addConfigBuilder(init: AppConfigBuilderCallback): void { - this.#configBuilders.push(init); - } - - setProxyPath(path: string) { - this.addConfigBuilder((builder) => { - builder.config.proxy = { path }; - }); - } - /** * WARNING: this function will be remove in future */ @@ -55,59 +47,41 @@ export class AppConfigurator implements IAppConfigurator { // TODO - remove when refactor portal service! /** resolve and create a client from discovery */ - try { - return await serviceDiscovery.createClient('app'); - } catch { - return await serviceDiscovery.createClient('portal'); - } + return await serviceDiscovery.createClient('apps'); } } - public async createConfig( + public setClient( + client_or_cb: + | Promise + | ConfigBuilderCallback, + ) { + const cb = typeof client_or_cb === 'object' ? () => client_or_cb : client_or_cb; + this._set('client', cb); + } + + // TODO - explain why, used in import of resources aka proxy url + public setAssetUri(base_or_cb: string | ConfigBuilderCallback) { + const cb = typeof base_or_cb === 'string' ? async () => base_or_cb : base_or_cb; + this._set('assetUri', cb); + } + + protected _createConfig( init: ModuleInitializerArgs, - ): Promise { - const config = await this.#configBuilders.reduce( - async (cur, cb) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const builder = new AppConfigBuilder(init, await cur); - await Promise.resolve(cb(builder)); - return Object.assign(cur, builder.config); - }, - Promise.resolve({} as Partial), - ); + initial?: Partial, + ) { + if (!this._has('client')) { + this.setClient(async () => { + const httpClient = await this._createHttpClient(init); + const appClient = new AppClient(httpClient); + return appClient; + }); + } - // TODO - make less lazy - config.client ??= await (async (): Promise => { - const httpClient = await this._createHttpClient(init); - return { - getAppManifest: { - client: { - fn: ({ appKey }) => httpClient.json$(`/api/apps/${appKey}`), - }, - key: ({ appKey }) => appKey, - expire: this.defaultExpireTime, - }, - getAppManifests: { - client: { - fn: () => httpClient.json$(`/api/apps`), - }, - // TODO - might cast to checksum - key: () => 'manifests', - expire: this.defaultExpireTime, - }, - getAppConfig: { - client: { - fn: ({ appKey, tag }) => - httpClient.json$( - `/api/apps/${appKey}/config${tag ? `?tag=${tag}` : ''}`, - ), - }, - key: (args) => JSON.stringify(args), - expire: this.defaultExpireTime, - }, - }; - })(); + if (!this._has('assetUri')) { + this.setAssetUri('/apps-proxy'); + } - return config as AppModuleConfig; + return super._createConfig(init, initial); } } diff --git a/packages/modules/app/src/AppModuleProvider.ts b/packages/modules/app/src/AppModuleProvider.ts index 36cb588d02..45345e4194 100644 --- a/packages/modules/app/src/AppModuleProvider.ts +++ b/packages/modules/app/src/AppModuleProvider.ts @@ -1,7 +1,7 @@ import { BehaviorSubject, - catchError, distinctUntilChanged, + from, map, Observable, pairwise, @@ -10,27 +10,23 @@ import { } from 'rxjs'; import { ModuleType } from '@equinor/fusion-framework-module'; -import { HttpResponseError } from '@equinor/fusion-framework-module-http'; import { EventModule } from '@equinor/fusion-framework-module-event'; -import { Query } from '@equinor/fusion-query'; - import type { AppConfig, AppManifest, CurrentApp } from './types'; import { App, filterEmpty, IApp } from './app/App'; import { AppModuleConfig } from './AppConfigurator'; -import { AppConfigError, AppManifestError } from './errors'; import { AppBundleStateInitial } from './app/types'; +import { IAppClient } from './AppClient'; export class AppModuleProvider { static compareAppManifest(a?: T, b?: T): boolean { return JSON.stringify(a) === JSON.stringify(b); } - public appClient: Query; - #appsClient: Query; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - #configClient: Query, { appKey: string; tag?: string }>; + #appClient: IAppClient; + + #appBaseUri: string; #current$: BehaviorSubject; @@ -63,17 +59,13 @@ export class AppModuleProvider { constructor(args: { config: AppModuleConfig; event?: ModuleType }) { const { event, config } = args; + this.#appClient = config.client; this.#event = event; this.#current$ = new BehaviorSubject(undefined); - this.appClient = new Query(config.client.getAppManifest); - this.#appsClient = new Query(config.client.getAppManifests); - this.#configClient = new Query(config.client.getAppConfig); + this.#appBaseUri = config.assetUri ?? ''; - this.#subscription.add(() => this.appClient.complete()); - this.#subscription.add(() => this.#appsClient.complete()); - this.#subscription.add(() => this.#configClient.complete()); this.#subscription.add( this.current$ .pipe( @@ -104,28 +96,19 @@ export class AppModuleProvider { * @param appKey - application key */ public getAppManifest(appKey: string): Observable { - return Query.extractQueryValue( - this.appClient.query({ appKey }).pipe( - catchError((err) => { - /** extract cause, since error will be a `QueryError` */ - const { cause } = err; - if (cause instanceof AppManifestError) { - throw cause; - } - if (cause instanceof HttpResponseError) { - throw AppManifestError.fromHttpResponse(cause.response, { cause }); - } - throw new AppManifestError('unknown', 'failed to load manifest', { cause }); - }), - ), - ); + return from(this.#appClient.getAppManifest({ appKey })); + } + + public getAppManifests(filter?: { filterByCurrentUser: boolean }): Observable { + return from(this.#appClient.getAppManifests(filter)); } /** * fetch all applications + * @deprecated use `getAppManifests` instead */ public getAllAppManifests(): Observable { - return Query.extractQueryValue(this.#appsClient.query()); + return this.getAppManifests(); } /** @@ -136,21 +119,7 @@ export class AppModuleProvider { appKey: string, tag?: string, ): Observable> { - return Query.extractQueryValue( - this.#configClient.query({ appKey, tag }).pipe( - catchError((err) => { - /** extract cause, since error will be a `QueryError` */ - const { cause } = err; - if (cause instanceof AppConfigError) { - throw cause; - } - if (cause instanceof HttpResponseError) { - throw AppConfigError.fromHttpResponse(cause.response, { cause }); - } - throw new AppConfigError('unknown', 'failed to load config', { cause }); - }), - ), - ); + return from(this.#appClient.getAppConfig({ appKey, tag })); } /** @@ -167,6 +136,10 @@ export class AppModuleProvider { this.#current$.next(null); } + public get assetUri(): string { + return this.#appBaseUri; + } + /** * This should not be used, only for legacy creation backdoor * @deprecated diff --git a/packages/modules/app/src/app/App.ts b/packages/modules/app/src/app/App.ts index 0891ce881a..a6293a4c83 100644 --- a/packages/modules/app/src/app/App.ts +++ b/packages/modules/app/src/app/App.ts @@ -1,4 +1,4 @@ -import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; +import type { AppConfig, AppModulesInstance, AppScriptModule } from '../types'; import { FlowSubject, Observable } from '@equinor/fusion-observable'; import type { AppModuleProvider } from '../AppModuleProvider'; @@ -8,6 +8,7 @@ import { firstValueFrom, lastValueFrom, map, + of, OperatorFunction, Subscription, } from 'rxjs'; @@ -16,6 +17,7 @@ import { AnyModule, ModuleType } from '@equinor/fusion-framework-module'; import { createState } from './create-state'; import { actions, Actions } from './actions'; import { AppBundleState, AppBundleStateInitial } from './types'; +import { AppManifest } from '../types'; import './events'; @@ -71,13 +73,13 @@ export interface IApp | unknown = * Gets the manifest of the app. * @returns The manifest of the app, or undefined if it doesn't exist. */ - get manifest(): AppManifest | undefined; + get manifest(): Readonly | undefined; /** * Retrieves the manifest asynchronously. * @returns A promise that resolves to the AppManifest. */ - get manifestAsync(): Promise; + get manifestAsync(): Promise>; /** * Gets the configuration of the app. @@ -438,7 +440,12 @@ export class App | unknown = unkno } public loadConfig() { - this.#state.next(actions.fetchConfig(this.appKey)); + // TODO - shit fix + (this.manifest ? of(this.manifest) : this.getManifest()).subscribe({ + next: (manifest) => { + this.#state.next(actions.fetchConfig(manifest)); + }, + }); } public loadManifest(update?: boolean) { @@ -451,7 +458,13 @@ export class App | unknown = unkno public async loadAppModule(allow_cache = true) { const manifest = await this.getManifestAsync(allow_cache); - this.#state.next(actions.importApp(manifest.entry)); + if (manifest.build?.entryPoint) { + this.#state.next(actions.importApp(manifest.build.entryPoint)); + } else { + console.log( + `The ${manifest.appKey} is missing entryPoint, please upload a build for the app before continuing`, + ); + } } public getConfig(force_refresh = false): Observable { @@ -602,8 +615,20 @@ export class App | unknown = unkno subscriber.add( // fetch application latest manifest and request loading of the application script this.getManifest().subscribe((manifest) => { - // dispatch import_app action to load the application script - this.#state.next(actions.importApp(manifest.entry)); + if (manifest.build?.entryPoint) { + // TODO - this should come from backend + const assetPath = + manifest.build.assetPath ?? + [manifest.appKey, manifest.build.version].join('@'); + // dispatch import_app action to load the application script + this.#state.next( + actions.importApp([assetPath, manifest.build.entryPoint].join('/')), + ); + } else { + console.error( + `The ${manifest.appKey} app is missing a entry in the manifest, upload a build for your app before continuing`, + ); + } }), ); }); diff --git a/packages/modules/app/src/app/actions.ts b/packages/modules/app/src/app/actions.ts index 9f15181611..62d4a66e7e 100644 --- a/packages/modules/app/src/app/actions.ts +++ b/packages/modules/app/src/app/actions.ts @@ -4,7 +4,7 @@ import { createAction, createAsyncAction, } from '@equinor/fusion-observable'; -import { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; +import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; const createActions = () => ({ /** Manifest loading */ @@ -27,7 +27,7 @@ const createActions = () => ({ setConfig: createAction('set_config', (config: AppConfig) => ({ payload: config })), fetchConfig: createAsyncAction( 'fetch_config', - (key: string) => ({ payload: key }), + (manifest: AppManifest) => ({ payload: manifest }), (config: AppConfig) => ({ payload: config }), (error: unknown) => ({ payload: error }), ), diff --git a/packages/modules/app/src/app/create-reducer.ts b/packages/modules/app/src/app/create-reducer.ts index b7191b0bbf..27bbcd4109 100644 --- a/packages/modules/app/src/app/create-reducer.ts +++ b/packages/modules/app/src/app/create-reducer.ts @@ -20,8 +20,9 @@ export const createReducer = (value: AppBundleStateInitial) => builder // update or set manifest .addCase(actions.setManifest, (state, action) => { + // TODO: after legacy is removed, remove the update flag if (action.meta.update) { - state.manifest = { ...state.manifest, ...action.payload }; + state.manifest = Object.assign(state.manifest ?? {}, action.payload); } else { state.manifest = action.payload; } diff --git a/packages/modules/app/src/app/create-state.ts b/packages/modules/app/src/app/create-state.ts index 7292cf7e0f..e09d8dfd52 100644 --- a/packages/modules/app/src/app/create-state.ts +++ b/packages/modules/app/src/app/create-state.ts @@ -24,7 +24,7 @@ export const createState = ( state.addFlow(handleFetchConfig(provider)); // add handler for loading application script - state.addFlow(handleImportApplication()); + state.addFlow(handleImportApplication(provider)); return state; }; diff --git a/packages/modules/app/src/app/flows.ts b/packages/modules/app/src/app/flows.ts index 3c9ad8cb7e..ab9fa1592e 100644 --- a/packages/modules/app/src/app/flows.ts +++ b/packages/modules/app/src/app/flows.ts @@ -66,20 +66,23 @@ export const handleFetchConfig = // only handle fetch config request actions filter(actions.fetchConfig.match), // when request is received, abort any ongoing request and start new - switchMap(({ payload: appKey }) => { - // fetch manifest from provider - const subject = from(provider.getAppConfig(appKey)).pipe( + switchMap(({ payload }) => { + // TODO - use the configUrl directly from the manifest + // fetch config from provider + const subject = from( + provider.getAppConfig(payload.appKey, payload.build?.version), + ).pipe( // filter out null values filter((x) => !!x), // allow multiple subscriptions share(), ); - // first load manifest and then dispatch success action + // first load config and then dispatch success action return concat( - subject.pipe(map((manifest) => actions.setConfig(manifest))), + subject.pipe(map((config) => actions.setConfig(config))), subject.pipe( last(), - map((manifest) => actions.fetchConfig.success(manifest)), + map((config) => actions.fetchConfig.success(config)), ), ).pipe( // catch any error and dispatch failure action @@ -94,18 +97,21 @@ export const handleFetchConfig = * Handles the import application flow. * @returns A flow that takes in actions and returns an observable of AppBundleState. */ -export const handleImportApplication = (): Flow => (action$) => - action$.pipe( - // only handle import script request actions - filter(actions.importApp.match), - // when request is received, abort any ongoing request and start new - switchMap(({ payload }) => { - // dynamically import the application script - return from(import(payload)).pipe( - // dispatch success action - map(actions.importApp.success), - // catch any error and dispatch failure action - catchError((err) => of(actions.importApp.failure(err))), - ); - }), - ); +export const handleImportApplication = + (provider: AppModuleProvider): Flow => + (action$) => + action$.pipe( + // only handle import script request actions + filter(actions.importApp.match), + // when request is received, abort any ongoing request and start new + switchMap(({ payload }) => { + const endpoint = [provider.assetUri, payload].join('/').replace(/\/{2,}/g, '/'); + // dynamically import the application script + return from(import(/* @vite-ignore */ endpoint)).pipe( + // dispatch success action + map(actions.importApp.success), + // catch any error and dispatch failure action + catchError((err) => of(actions.importApp.failure(err))), + ); + }), + ); diff --git a/packages/modules/app/src/app/types.ts b/packages/modules/app/src/app/types.ts index df1c0ef718..0590ce39e5 100644 --- a/packages/modules/app/src/app/types.ts +++ b/packages/modules/app/src/app/types.ts @@ -1,4 +1,4 @@ -import type { AppConfig, AppManifest, AppModulesInstance, AppScriptModule } from '../types'; +import type { AppManifest, AppConfig, AppModulesInstance, AppScriptModule } from '../types'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type AppBundleState = { diff --git a/packages/modules/app/src/application.schema.ts b/packages/modules/app/src/application.schema.ts new file mode 100644 index 0000000000..b6c9330c73 --- /dev/null +++ b/packages/modules/app/src/application.schema.ts @@ -0,0 +1,58 @@ +import z from 'zod'; + +const ApiApplicationPersonSchema = z.object({ + azureUniqueId: z.string(), + displayName: z.string(), + mail: z.string().nullish(), + upn: z.string().nullish(), + accountType: z.string(), + accountClassification: z.string().nullish(), + isExpired: z.boolean().nullish(), +}); + +export const ApiApplicationBuildSchema = z.object({ + version: z.string(), + entryPoint: z.string(), + tags: z.array(z.string()).nullish(), + tag: z.enum(['latest', 'preview']).nullish(), + assetPath: z.string().nullish(), + configUrl: z.string().nullish(), + timestamp: z.string().nullish(), + commitSha: z.string().nullish(), + githubRepo: z.string().nullish(), + projectPage: z.string().nullish(), + allowedExtensions: z.array(z.string()).nullish(), + uploadedBy: ApiApplicationPersonSchema.nullish(), +}); + +export const ApiApplicationSchema = z.object({ + appKey: z.string(), + displayName: z.string(), + description: z.string(), + type: z.string(), + isPinned: z.boolean().nullish(), + templateSource: z.string().nullish(), + category: z + .object({ + id: z.string(), + name: z.string(), + displayName: z.string(), + color: z.string(), + defaultIcon: z.string(), + sortOrder: z.number(), + }) + .nullish(), + visualization: z + .object({ + color: z.string().nullish(), + icon: z.string().nullish(), + sortOrder: z.number(), + }) + .nullish(), + keywords: z.array(z.string()).nullish(), + admins: z.array(ApiApplicationPersonSchema).nullish(), + owners: z.array(ApiApplicationPersonSchema).nullish(), + build: ApiApplicationBuildSchema.nullish(), +}); + +export default { ApiApplicationPersonSchema, ApiApplicationBuildSchema, ApiApplicationSchema }; diff --git a/packages/modules/app/src/enable-app-module.ts b/packages/modules/app/src/enable-app-module.ts index 97b9be9ffd..f0177a84ce 100644 --- a/packages/modules/app/src/enable-app-module.ts +++ b/packages/modules/app/src/enable-app-module.ts @@ -1,7 +1,6 @@ import type { IModulesConfigurator } from '@equinor/fusion-framework-module'; -import type { AppConfigBuilderCallback } from './AppConfigBuilder'; - import { module } from './module'; +import { AppConfigurator } from './AppConfigurator'; /** * Method for enabling the Service module @@ -10,12 +9,14 @@ import { module } from './module'; export const enableAppModule = ( // eslint-disable-next-line @typescript-eslint/no-explicit-any configurator: IModulesConfigurator, - builder?: AppConfigBuilderCallback, + callback?: (builder: AppConfigurator) => void | Promise, ): void => { configurator.addConfig({ module, - configure: (appConfigurator) => { - builder && appConfigurator.addConfigBuilder(builder); + configure: async (configurator) => { + if (callback) { + Promise.resolve(callback(configurator)); + } }, }); }; diff --git a/packages/modules/app/src/helpers.ts b/packages/modules/app/src/helpers.ts deleted file mode 100644 index 5b8b2694e6..0000000000 --- a/packages/modules/app/src/helpers.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AppManifest } from './types'; - -// manifest properties to compare -const compareKey: Array = ['key', 'version']; - -/** - * Compares two app manifests for equality. - * - * @template T - The type of the app manifest. - * @param a - The first app manifest. - * @param b - The second app manifest. - * @returns True if the app manifests are equal, false otherwise. - */ -export const compareAppManifest = (a?: T, b?: T): boolean => { - // use compareKey to compare only the keys that are important for equality - return compareKey.every((key) => a?.[key] === b?.[key]); -}; diff --git a/packages/modules/app/src/index.ts b/packages/modules/app/src/index.ts index 8b9e2ef088..eef9bece9a 100644 --- a/packages/modules/app/src/index.ts +++ b/packages/modules/app/src/index.ts @@ -2,8 +2,11 @@ export { AppModuleConfig, AppConfigurator, IAppConfigurator, - AppModuleConfig as IAppModuleConfig, + type AppModuleConfig as IAppModuleConfig, } from './AppConfigurator'; + +export { AppClient, type IAppClient } from './AppClient'; + export { AppModuleProvider } from './AppModuleProvider'; export { IApp } from './app/App'; diff --git a/packages/modules/app/src/module.ts b/packages/modules/app/src/module.ts index 6d21b5b060..d2d5e0103d 100644 --- a/packages/modules/app/src/module.ts +++ b/packages/modules/app/src/module.ts @@ -1,12 +1,12 @@ import { Module } from '@equinor/fusion-framework-module'; import { ModuleDeps } from './types'; -import { IAppConfigurator, AppConfigurator } from './AppConfigurator'; +import { AppConfigurator } from './AppConfigurator'; import { AppModuleProvider } from './AppModuleProvider'; export const moduleKey = 'app'; -export type AppModule = Module; +export type AppModule = Module; /** * Represents a module for handling applications. @@ -29,7 +29,7 @@ export const module: AppModule = { * @returns A new instance of AppModuleProvider. */ initialize: async (args) => { - const config = await (args.config as AppConfigurator).createConfig(args); + const config = await args.config.createConfigAsync(args); const event = await args.requireInstance('event').catch(() => undefined); return new AppModuleProvider({ config, event }); }, diff --git a/packages/modules/app/src/types.ts b/packages/modules/app/src/types.ts index 6919540a62..ac82614620 100644 --- a/packages/modules/app/src/types.ts +++ b/packages/modules/app/src/types.ts @@ -19,7 +19,8 @@ export type AppEnv = { // TODO: change to module-services when new app service is created export type ModuleDeps = [HttpModule, ServiceDiscoveryModule, EventModule]; -export type AppType = 'standalone' | 'report' | 'launcher'; +// TODO: remove `report` and `launcher` when legacy apps are removed +export type AppType = 'standalone' | 'report' | 'launcher' | 'template'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type CurrentApp = [], TEnv = any> = @@ -27,43 +28,74 @@ export type CurrentApp = [], TEnv = any> = | null | undefined; -export type AppAuth = { - clientId: string; - resources: string[]; -}; +type Nullable = T | null | undefined; -type AppCategory = { - id?: string; - name: string | null; - color: string | null; - defaultIcon: string | null; +type AppPerson = { + id: string; + azureUniqueId: string; + displayName: string; + mail?: Nullable; + upn?: Nullable; + accountType: string; + accountClassification?: Nullable; + isExpired?: Nullable; }; -export type AppManifest = { - key: string; - name: string; - entry: string; +export type AppAdmin = AppPerson; + +export type AppOwner = AppPerson; + +export type AppBuildManifest = { version: string; - shortName?: string; - description?: string; - type?: AppType; - tags?: string[]; - // context?: ContextManifest; - auth?: AppAuth[]; - icon?: string; - order?: number; - publishedDate?: Date; - accentColor?: string; - categoryId?: string; - category?: AppCategory; - hide?: boolean; + entryPoint: string; + tags?: Nullable; + tag?: Nullable<'latest' | 'preview'>; + assetPath?: Nullable; + configUrl?: Nullable; + timestamp?: Nullable; + commitSha?: Nullable; + githubRepo?: Nullable; + projectPage?: Nullable; + annotations?: Nullable>; + allowedExtensions?: Nullable; + uploadedBy?: Nullable; }; -export type Endpoint = { name: string; uri: string; scopes?: string[] }; +export interface AppManifest { + /** @deprecated will be removed, use appKey */ + key?: string; + appKey: string; + /** @deprecated will be removed, use displayName */ + name?: string; + displayName: string; + description: string; + type: AppType; + isPinned?: Nullable; + templateSource?: Nullable; + category?: Nullable<{ + id: string; + name: string; + displayName: string; + color: string; + defaultIcon: string; + sortOrder: number; + }>; + visualization?: Nullable<{ + color?: Nullable; + icon?: Nullable; + sortOrder: number; + }>; + keywords?: Nullable; + admins?: Nullable; + owners?: Nullable; + build?: Nullable; +} + +export type Endpoint = { url: string; scopes?: string[] }; export type AppConfig = { - environment: TEnvironment; - endpoints: Record; + environment?: TEnvironment; + endpoints?: Record; }; /** diff --git a/packages/modules/service-discovery/src/configurator.ts b/packages/modules/service-discovery/src/configurator.ts index 6531e21ee3..753924125b 100644 --- a/packages/modules/service-discovery/src/configurator.ts +++ b/packages/modules/service-discovery/src/configurator.ts @@ -24,10 +24,13 @@ export class ServiceDiscoveryConfigurator extends BaseConfigBuilder { + // TODO: should unnamed application be allowed to use feature flags? builder.addPlugin( createLocalStoragePlugin(localFlags, { - name: (configurator as AppConfigurator).env?.manifest.key, + name: (configurator as AppConfigurator).env?.manifest.appKey ?? undefined, }), ); builder.addPlugin(createUrlPlugin(urlFlags)); diff --git a/packages/react/app/src/index.ts b/packages/react/app/src/index.ts index 60439f3889..d0261c52b7 100644 --- a/packages/react/app/src/index.ts +++ b/packages/react/app/src/index.ts @@ -4,11 +4,12 @@ export type { AppModuleInitiator, AppModules, AppModulesInstance, - AppManifest, AppRenderFn, IAppConfigurator, } from '@equinor/fusion-framework-app'; +export { AppManifest } from '@equinor/fusion-framework-module-app'; + export { useAppModule } from './useAppModule'; export { useAppModules } from './useAppModules'; export { useAppEnvironmentVariables } from './useAppEnvironmentVariables'; diff --git a/packages/react/components/bookmark/src/components/create-bookmark/CreateBookmark.tsx b/packages/react/components/bookmark/src/components/create-bookmark/CreateBookmark.tsx index 717c629aa4..d46023f32d 100644 --- a/packages/react/components/bookmark/src/components/create-bookmark/CreateBookmark.tsx +++ b/packages/react/components/bookmark/src/components/create-bookmark/CreateBookmark.tsx @@ -58,7 +58,7 @@ export const CreateBookmarkModal = ({
diff --git a/packages/react/components/bookmark/src/components/edit-bookmark/EditBookmark.tsx b/packages/react/components/bookmark/src/components/edit-bookmark/EditBookmark.tsx index 2f6c73f3b1..bbab400f56 100644 --- a/packages/react/components/bookmark/src/components/edit-bookmark/EditBookmark.tsx +++ b/packages/react/components/bookmark/src/components/edit-bookmark/EditBookmark.tsx @@ -90,7 +90,7 @@ export const EditBookmarkModal = ({
diff --git a/packages/react/framework/src/app/useApps.ts b/packages/react/framework/src/app/useApps.ts index afac51dfe4..d199d70916 100644 --- a/packages/react/framework/src/app/useApps.ts +++ b/packages/react/framework/src/app/useApps.ts @@ -4,27 +4,37 @@ import { useMemo } from 'react'; import { useAppProvider } from './useAppProvider'; +type UseAppsArgs = { + /** @deprecated - no longer available */ + includeHidden?: boolean; + // only show apps that the current user has access to + filterByCurrentUser?: boolean; +}; + /** * React Hook - Get apps from framework - * @param args Object with boolean member includeHidden - * @returns Object {apps, isLoading} where apps is Array of AppManifest, isLoading is a boolean on observable complete + * @param args Object with filterByCurrentUser: boolean + * @returns Object {apps, isLoading, error} where apps is Array of ApplicationManifest, isLoading is a boolean on observable complete + * @since 7.1.1 */ -export const useApps = (args?: { - includeHidden: boolean; -}): { apps: AppManifest[] | undefined; isLoading: boolean; error: unknown } => { +export const useApps = ( + args?: UseAppsArgs, +): { apps: AppManifest[] | undefined; isLoading: boolean; error: unknown } => { const provider = useAppProvider(); + + const { filterByCurrentUser } = args || {}; + const { - value: manifests, + value: apps, complete, error, - } = useObservableState(useMemo(() => provider.getAllAppManifests(), [provider])); - - const apps = useMemo(() => { - if (manifests && !args?.includeHidden) { - return manifests.filter((app) => !app.hide); - } - return manifests; - }, [args, manifests]); + } = useObservableState( + useMemo( + () => + provider.getAppManifests(filterByCurrentUser ? { filterByCurrentUser } : undefined), + [provider, filterByCurrentUser], + ), + ); return { apps, isLoading: !complete, error }; }; diff --git a/packages/react/legacy-interopt/src/LegacyAppContainer.ts b/packages/react/legacy-interopt/src/LegacyAppContainer.ts index a9106feea1..59c0a83438 100644 --- a/packages/react/legacy-interopt/src/LegacyAppContainer.ts +++ b/packages/react/legacy-interopt/src/LegacyAppContainer.ts @@ -7,7 +7,7 @@ import { TelemetryLogger, } from '@equinor/fusion'; -import { AppManifest } from '@equinor/fusion-framework-app'; +import { AppManifest } from '@equinor/fusion-framework-module-app'; import { ActionTypes, createAction, createReducer, FlowSubject } from '@equinor/fusion-observable'; import { original } from 'immer'; @@ -249,7 +249,7 @@ export class LegacyAppContainer extends EventEmitter { selectedApp: current ? { key: current.appKey, - name: current.manifest?.name ?? null, + name: current.manifest?.displayName ?? null, } : null, previousApps: Object.keys(previousApps.state).map((key) => ({ @@ -284,11 +284,11 @@ export class LegacyAppContainer extends EventEmitter { async setCurrentAppAsync(appKey: string | null): Promise { if (appKey) { - const manifest = this.get(appKey) as unknown as AppManifest; + const manifest = this.get(appKey); const appProvider = this.#framework.modules.app; const currentApp = appProvider.current; if (currentApp && currentApp.appKey === appKey) { - currentApp.updateManifest(manifest); + currentApp.updateManifest(manifest as unknown as AppManifest); await currentApp.getConfigAsync(); } else { if (currentApp?.appKey !== appKey) { @@ -303,7 +303,7 @@ export class LegacyAppContainer extends EventEmitter { 'these lines should newer been reached', ); } - const newApp = appProvider.createApp({ appKey, manifest }); + const newApp = appProvider.createApp({ appKey }); await newApp.getConfigAsync(); appProvider.setCurrentApp(newApp); } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c878fbe285..c0add89e4d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 2.27.8 '@commitlint/cli': specifier: ^19.0.3 - version: 19.5.0(@types/node@22.7.4)(typescript@5.6.2) + version: 19.4.0(@types/node@22.5.0)(typescript@5.6.2) '@commitlint/config-conventional': specifier: ^19.0.3 version: 19.5.0 @@ -31,7 +31,7 @@ importers: version: 7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) '@vitest/coverage-v8': specifier: ^2.0.1 - version: 2.0.5(vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)) + version: 2.0.5(vitest@2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)) eslint: specifier: ^8.47.0 version: 8.57.1 @@ -70,16 +70,16 @@ importers: version: 2.7.0 turbo: specifier: ^2.0.6 - version: 2.1.3 + version: 2.1.2 typescript: specifier: ^5.5.4 version: 5.6.2 vitest: specifier: ^2.0.5 - version: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) + version: 2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) vitest-github-actions-reporter: specifier: ^0.11.1 - version: 0.11.1(vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)) + version: 0.11.1(vitest@2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)) cookbooks/app-react: devDependencies: @@ -109,13 +109,13 @@ importers: dependencies: '@ag-grid-community/client-side-row-model': specifier: ~32.2.0 - version: 32.2.0 + version: 32.2.1 '@ag-grid-community/core': specifier: ~32.2.0 - version: 32.2.0 + version: 32.2.1 '@ag-grid-community/react': specifier: ~32.2.0 - version: 32.2.0(@ag-grid-community/core@32.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 32.2.1(@ag-grid-community/core@32.2.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@ag-grid-community/styles': specifier: ^32.1.0 version: 32.1.0 @@ -166,6 +166,30 @@ importers: specifier: ^5.5.4 version: 5.5.4 + cookbooks/app-react-assets: + devDependencies: + '@equinor/fusion-framework-cli': + specifier: workspace:^ + version: link:../../packages/cli + '@equinor/fusion-framework-react-app': + specifier: workspace:^ + version: link:../../packages/react/app + '@types/react': + specifier: ^18.2.50 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.2.7 + version: 18.3.0 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + typescript: + specifier: ^5.5.4 + version: 5.6.2 + cookbooks/app-react-bookmark: devDependencies: '@equinor/fusion-framework-cli': @@ -534,6 +558,61 @@ importers: specifier: ^5.5.4 version: 5.5.4 + cookbooks/poc-portal: + dependencies: + '@equinor/fusion-framework': + specifier: workspace:* + version: link:../../packages/framework + '@equinor/fusion-framework-cli': + specifier: workspace:* + version: link:../../packages/cli + '@equinor/fusion-framework-module': + specifier: workspace:* + version: link:../../packages/modules/module + '@equinor/fusion-framework-module-app': + specifier: workspace:* + version: link:../../packages/modules/app + '@equinor/fusion-framework-module-http': + specifier: workspace:* + version: link:../../packages/modules/http + '@equinor/fusion-framework-module-msal': + specifier: workspace:* + version: link:../../packages/modules/msal + '@equinor/fusion-framework-module-service-discovery': + specifier: workspace:* + version: link:../../packages/modules/service-discovery + '@equinor/fusion-framework-react': + specifier: workspace:* + version: link:../../packages/react/framework + '@equinor/fusion-framework-react-app': + specifier: workspace:* + version: link:../../packages/react/app + '@vitejs/plugin-react': + specifier: ^4.3.1 + version: 4.3.1(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8)) + is-mergeable-object: + specifier: ^1.1.1 + version: 1.1.1 + react: + specifier: ^18.2.0 + version: 18.3.1 + react-dom: + specifier: ^18.2.0 + version: 18.3.1(react@18.3.1) + vite: + specifier: ^5.3.4 + version: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) + vite-plugin-environment: + specifier: ^1.1.3 + version: 1.1.3(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8)) + devDependencies: + '@types/react': + specifier: ^18.2.50 + version: 18.3.3 + '@types/react-dom': + specifier: ^18.3.0 + version: 18.3.0 + packages/app: dependencies: '@equinor/fusion-framework': @@ -564,39 +643,6 @@ importers: packages/cli: dependencies: - '@equinor/eds-core-react': - specifier: ^0.42.0 - version: 0.42.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) - '@equinor/eds-icons': - specifier: ^0.21.0 - version: 0.21.0 - '@equinor/eds-tokens': - specifier: ^0.9.2 - version: 0.9.2 - '@equinor/fusion-framework-app': - specifier: workspace:^ - version: link:../app - '@equinor/fusion-framework-module-feature-flag': - specifier: workspace:^ - version: link:../modules/feature-flag - '@equinor/fusion-framework-react-components-people-provider': - specifier: workspace:^ - version: link:../react/components/people-resolver - '@equinor/fusion-observable': - specifier: workspace:^ - version: link:../utils/observable - '@equinor/fusion-wc-chip': - specifier: ^1.2.1 - version: 1.2.1 - '@equinor/fusion-wc-person': - specifier: ^3.0.3 - version: 3.0.3 - '@types/adm-zip': - specifier: ^0.5.0 - version: 0.5.5 - '@types/semver': - specifier: ^7.5.0 - version: 7.5.8 '@vitejs/plugin-react': specifier: ^4.0.4 version: 4.3.1(vite@5.4.3(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8)) @@ -612,21 +658,21 @@ importers: deepmerge: specifier: ^4.3.1 version: 4.3.1 - express: - specifier: ^4.18.2 - version: 4.19.2 - express-rate-limit: - specifier: ^7.0.0 - version: 7.4.0(express@4.19.2) find-up: specifier: ^7.0.0 version: 7.0.0 - http-proxy-middleware: - specifier: ^2.0.6 - version: 2.0.6(@types/express@4.17.21) is-mergeable-object: specifier: ^1.1.1 version: 1.1.1 + loader-utils: + specifier: ^3.3.1 + version: 3.3.1 + mime: + specifier: ^4.0.4 + version: 4.0.4 + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 ora: specifier: ^8.0.1 version: 8.0.1 @@ -655,9 +701,24 @@ importers: specifier: ^4.2.0 version: 4.3.2(typescript@5.5.4)(vite@5.4.3(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8)) devDependencies: + '@equinor/eds-core-react': + specifier: ^0.41.2 + version: 0.41.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@equinor/eds-icons': + specifier: ^0.21.0 + version: 0.21.0 + '@equinor/eds-tokens': + specifier: ^0.9.2 + version: 0.9.2 + '@equinor/eslint-config-fusion-react': + specifier: ^2.0.1 + version: 2.1.1(@equinor/eslint-config-fusion@2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.5.4))(eslint@8.57.1) '@equinor/fusion-framework': specifier: workspace:^ version: link:../framework + '@equinor/fusion-framework-app': + specifier: workspace:^ + version: link:../app '@equinor/fusion-framework-module-app': specifier: workspace:^ version: link:../modules/app @@ -667,9 +728,9 @@ importers: '@equinor/fusion-framework-module-context': specifier: workspace:^ version: link:../modules/context - '@equinor/fusion-framework-module-http': + '@equinor/fusion-framework-module-feature-flag': specifier: workspace:^ - version: link:../modules/http + version: link:../modules/feature-flag '@equinor/fusion-framework-module-msal': specifier: workspace:^ version: link:../modules/msal @@ -685,9 +746,15 @@ importers: '@equinor/fusion-framework-react-components-bookmark': specifier: workspace:^ version: link:../react/components/bookmark + '@equinor/fusion-framework-react-components-people-provider': + specifier: workspace:^ + version: link:../react/components/people-resolver '@equinor/fusion-framework-react-module-bookmark': specifier: workspace:^ version: link:../react/modules/bookmark + '@equinor/fusion-observable': + specifier: workspace:^ + version: link:../utils/observable '@equinor/fusion-query': specifier: workspace:^ version: link:../utils/query @@ -699,16 +766,25 @@ importers: version: 0.3.0(react@18.3.1) '@equinor/fusion-react-side-sheet': specifier: 1.3.3 - version: 1.3.3(@equinor/eds-core-react@0.42.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@equinor/eds-icons@0.21.0)(@equinor/eds-tokens@0.9.2)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.3.3(@equinor/eds-core-react@0.41.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@equinor/eds-icons@0.21.0)(@equinor/eds-tokens@0.9.2)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@equinor/fusion-react-styles': specifier: ^0.6.0 version: 0.6.2(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@equinor/fusion-wc-chip': + specifier: ^1.2.1 + version: 1.2.1 + '@equinor/fusion-wc-person': + specifier: ^3.0.1 + version: 3.0.1 '@material-ui/styles': specifier: ^4.11.5 version: 4.11.5(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@types/express': - specifier: ^4.17.17 - version: 4.17.21 + '@types/adm-zip': + specifier: ^0.5.0 + version: 0.5.5 + '@types/loader-utils': + specifier: ^2.0.6 + version: 2.0.6 '@types/node': specifier: ^20.11.14 version: 20.14.12 @@ -718,6 +794,15 @@ importers: '@types/react-dom': specifier: ^18.2.7 version: 18.3.0 + '@types/rollup': + specifier: ^0.54.0 + version: 0.54.0 + '@types/semver': + specifier: ^7.5.0 + version: 7.5.8 + eslint-plugin-rxjs: + specifier: ^5.0.3 + version: 5.0.3(eslint@8.57.1)(typescript@5.5.4) react: specifier: ^18.2.0 version: 18.3.1 @@ -728,8 +813,8 @@ importers: specifier: ^6.15.0 version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) rollup: - specifier: ^4.3.0 - version: 4.19.0 + specifier: ^4.22.4 + version: 4.22.4 rxjs: specifier: ^7.8.1 version: 7.8.1 @@ -775,7 +860,7 @@ importers: dependencies: '@ag-grid-enterprise/core': specifier: ~32.2.0 - version: 32.2.0 + version: 32.2.1 devDependencies: '@equinor/fusion-framework-module': specifier: workspace:^ @@ -798,6 +883,9 @@ importers: rxjs: specifier: ^7.8.1 version: 7.8.1 + zod: + specifier: ^3.23.8 + version: 3.23.8 devDependencies: '@equinor/fusion-framework-module': specifier: workspace:^ @@ -950,7 +1038,7 @@ importers: version: 5.5.4 vitest: specifier: ^2.0.5 - version: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) + version: 2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) packages/modules/module: dependencies: @@ -1711,7 +1799,7 @@ importers: devDependencies: '@vuepress/bundler-vite': specifier: 2.0.0-rc.15 - version: 2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0) + version: 2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0) '@vuepress/cli': specifier: 2.0.0-rc.15 version: 2.0.0-rc.15(typescript@5.5.4) @@ -1720,7 +1808,7 @@ importers: version: 2.0.0-rc.15(typescript@5.5.4) '@vuepress/plugin-register-components': specifier: 2.0.0-rc.44 - version: 2.0.0-rc.44(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + version: 2.0.0-rc.44(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vuepress/utils': specifier: 2.0.0-rc.15 version: 2.0.0-rc.15 @@ -1735,10 +1823,10 @@ importers: version: 3.4.38(typescript@5.5.4) vuepress: specifier: 2.0.0-rc.15 - version: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + version: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) vuepress-theme-hope: specifier: 2.0.0-rc.52 - version: 2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + version: 2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) packages: @@ -1748,22 +1836,22 @@ packages: '@actions/http-client@2.2.1': resolution: {integrity: sha512-KhC/cZsq7f8I4LfZSJKgCvEwfkE8o1538VoBeoGzokVLLnbFDEAdFD3UhoMklxo2un9NJVBdANOresx7vTHlHw==} - '@ag-grid-community/client-side-row-model@32.2.0': - resolution: {integrity: sha512-Wg/jOd9+luIvIw5Ha+q4hTLMq1zSYtOrSwWZTGS8TIiLcAP/zScoPaweYrcEbWcR72aQ4XmSEcwwgluiwTbPtQ==} + '@ag-grid-community/client-side-row-model@32.2.1': + resolution: {integrity: sha512-FXM0wlDhRN+6vZ9GjswqpTEyZ/5zStTt0OtuKeNKwxZKodjHFJ8tNxImLqPnFTwreSBZjup4LU5PuwXABHYRig==} '@ag-grid-community/core@32.0.2': resolution: {integrity: sha512-Pm4KpWuoHtn/eXgzunX0R10TZt6ze3KIz89Q/pzl2ZE+GdEjn6vPmgG+lMb/z90FgFwnMuIOQiz1qk0x5/xaHA==} - '@ag-grid-community/core@32.2.0': - resolution: {integrity: sha512-ng7qhDfF91ecT5TldQsb5lhJ6M1ZhSIB2c1hSSmY0kqQHxn0uAJo6lpjz7SIUqyy4PFupsOoPs98CsW0Dl9dHA==} + '@ag-grid-community/core@32.2.1': + resolution: {integrity: sha512-60tz1DfCd3B/U9BkzOWwpaGZEQwvZIvwpf5n/GDP0yzxjNhCcUusQhllqqA3fmGKKke9O1anUdSQot4PfTTrOQ==} '@ag-grid-community/csv-export@32.0.2': resolution: {integrity: sha512-OUvFiq6C/uwo2AQi+xisnECXDRnskmFPjp0BzK/3ybstAZuF3tpYcM57UEmnAR3u3Gnav+MOfKmZTCSL8c30fw==} - '@ag-grid-community/react@32.2.0': - resolution: {integrity: sha512-Lx1c9gHzIwhlxivCd6g4bBAQMyNLQCS+GJ6QkhddKoT2TUQcl/9ExMOptbrC/FguHP3gdYKntVKMcp/xmNmSSg==} + '@ag-grid-community/react@32.2.1': + resolution: {integrity: sha512-DE5LLjEEaNNGXWP5AI44JiI7WoLZt31hwi+z61EoEuAxwFFrVlH5JnDRyo3ehGwHf7+28XRz50uY+wSYXNtGJQ==} peerDependencies: - '@ag-grid-community/core': 32.2.0 + '@ag-grid-community/core': 32.2.1 react: ^16.3.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.3.0 || ^17.0.0 || ^18.0.0 @@ -1779,8 +1867,8 @@ packages: '@ag-grid-enterprise/core@32.0.2': resolution: {integrity: sha512-ksHpdHqgg7OR5XMQWZ51vvvN8APhYkbtwuHG7B6QNP6JmdVfa88kMCpvb08So/JR4lIlvhunKHhosI41EjSBkw==} - '@ag-grid-enterprise/core@32.2.0': - resolution: {integrity: sha512-3u6SqgrXMOV0Xmf0zK0zvtnlGzi9cGf+M9qvMN+bcBR64Hc8zjmOTwUGdpUtJgs7R1vX5vVoZUK39jEp2tMFhQ==} + '@ag-grid-enterprise/core@32.2.1': + resolution: {integrity: sha512-dFSmDW+ftEkmhXW6qRelvMP2cUsyN34xjS3pDp65aFKJncsxO/9A2F7uUqB/GBItuAzR1YC8Xk/WYoGPM8mpZA==} '@ag-grid-enterprise/excel-export@32.0.2': resolution: {integrity: sha512-I3Gdfk3SSBZK2h32gr3p/FD5AICyRf3Gmu+sPjf3USlW2SwIl7h6j+/sgsEsm1XuLyrWjBFRJzpJjCiMGMvCPQ==} @@ -1912,8 +2000,8 @@ packages: resolution: {integrity: sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==} engines: {node: '>=6.9.0'} - '@babel/runtime@7.25.4': - resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} + '@babel/runtime@7.25.0': + resolution: {integrity: sha512-7dRy4DwXwtzBrPbZflqxnvfxLF8kdZXPkhymtDeFoFqE6ldzjQFgYTtYIFARcLEYDrqfBfYcZt1WqFxRoyC9Rw==} engines: {node: '>=6.9.0'} '@babel/runtime@7.25.6': @@ -2018,8 +2106,8 @@ packages: '@chevrotain/utils@11.0.3': resolution: {integrity: sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==} - '@commitlint/cli@19.5.0': - resolution: {integrity: sha512-gaGqSliGwB86MDmAAKAtV9SV1SHdmN8pnGq4EJU4+hLisQ7IFfx4jvU4s+pk6tl0+9bv6yT+CaZkufOinkSJIQ==} + '@commitlint/cli@19.4.0': + resolution: {integrity: sha512-sJX4J9UioVwZHq7JWM9tjT5bgWYaIN3rC4FP7YwfEwBYiIO+wMyRttRvQLNkow0vCdM0D67r9NEWU0Ui03I4Eg==} engines: {node: '>=v18'} hasBin: true @@ -2027,60 +2115,64 @@ packages: resolution: {integrity: sha512-OBhdtJyHNPryZKg0fFpZNOBM1ZDbntMvqMuSmpfyP86XSfwzGw4CaoYRG4RutUPg0BTK07VMRIkNJT6wi2zthg==} engines: {node: '>=v18'} - '@commitlint/config-validator@19.5.0': - resolution: {integrity: sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==} + '@commitlint/config-validator@19.0.3': + resolution: {integrity: sha512-2D3r4PKjoo59zBc2auodrSCaUnCSALCx54yveOFwwP/i2kfEAQrygwOleFWswLqK0UL/F9r07MFi5ev2ohyM4Q==} + engines: {node: '>=v18'} + + '@commitlint/ensure@19.0.3': + resolution: {integrity: sha512-SZEpa/VvBLoT+EFZVb91YWbmaZ/9rPH3ESrINOl0HD2kMYsjvl0tF7nMHh0EpTcv4+gTtZBAe1y/SS6/OhfZzQ==} engines: {node: '>=v18'} - '@commitlint/ensure@19.5.0': - resolution: {integrity: sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==} + '@commitlint/execute-rule@19.0.0': + resolution: {integrity: sha512-mtsdpY1qyWgAO/iOK0L6gSGeR7GFcdW7tIjcNFxcWkfLDF5qVbPHKuGATFqRMsxcO8OUKNj0+3WOHB7EHm4Jdw==} engines: {node: '>=v18'} - '@commitlint/execute-rule@19.5.0': - resolution: {integrity: sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==} + '@commitlint/format@19.3.0': + resolution: {integrity: sha512-luguk5/aF68HiF4H23ACAfk8qS8AHxl4LLN5oxPc24H+2+JRPsNr1OS3Gaea0CrH7PKhArBMKBz5RX9sA5NtTg==} engines: {node: '>=v18'} - '@commitlint/format@19.5.0': - resolution: {integrity: sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==} + '@commitlint/is-ignored@19.2.2': + resolution: {integrity: sha512-eNX54oXMVxncORywF4ZPFtJoBm3Tvp111tg1xf4zWXGfhBPKpfKG6R+G3G4v5CPlRROXpAOpQ3HMhA9n1Tck1g==} engines: {node: '>=v18'} - '@commitlint/is-ignored@19.5.0': - resolution: {integrity: sha512-0XQ7Llsf9iL/ANtwyZ6G0NGp5Y3EQ8eDQSxv/SRcfJ0awlBY4tHFAvwWbw66FVUaWICH7iE5en+FD9TQsokZ5w==} + '@commitlint/lint@19.2.2': + resolution: {integrity: sha512-xrzMmz4JqwGyKQKTpFzlN0dx0TAiT7Ran1fqEBgEmEj+PU98crOFtysJgY+QdeSagx6EDRigQIXJVnfrI0ratA==} engines: {node: '>=v18'} - '@commitlint/lint@19.5.0': - resolution: {integrity: sha512-cAAQwJcRtiBxQWO0eprrAbOurtJz8U6MgYqLz+p9kLElirzSCc0vGMcyCaA1O7AqBuxo11l1XsY3FhOFowLAAg==} + '@commitlint/load@19.4.0': + resolution: {integrity: sha512-I4lCWaEZYQJ1y+Y+gdvbGAx9pYPavqZAZ3/7/8BpWh+QjscAn8AjsUpLV2PycBsEx7gupq5gM4BViV9xwTIJuw==} engines: {node: '>=v18'} - '@commitlint/load@19.5.0': - resolution: {integrity: sha512-INOUhkL/qaKqwcTUvCE8iIUf5XHsEPCLY9looJ/ipzi7jtGhgmtH7OOFiNvwYgH7mA8osUWOUDV8t4E2HAi4xA==} + '@commitlint/message@19.0.0': + resolution: {integrity: sha512-c9czf6lU+9oF9gVVa2lmKaOARJvt4soRsVmbR7Njwp9FpbBgste5i7l/2l5o8MmbwGh4yE1snfnsy2qyA2r/Fw==} engines: {node: '>=v18'} - '@commitlint/message@19.5.0': - resolution: {integrity: sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==} + '@commitlint/parse@19.0.3': + resolution: {integrity: sha512-Il+tNyOb8VDxN3P6XoBBwWJtKKGzHlitEuXA5BP6ir/3loWlsSqDr5aecl6hZcC/spjq4pHqNh0qPlfeWu38QA==} engines: {node: '>=v18'} - '@commitlint/parse@19.5.0': - resolution: {integrity: sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==} + '@commitlint/read@19.4.0': + resolution: {integrity: sha512-r95jLOEZzKDakXtnQub+zR3xjdnrl2XzerPwm7ch1/cc5JGq04tyaNpa6ty0CRCWdVrk4CZHhqHozb8yZwy2+g==} engines: {node: '>=v18'} - '@commitlint/read@19.5.0': - resolution: {integrity: sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==} + '@commitlint/resolve-extends@19.1.0': + resolution: {integrity: sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg==} engines: {node: '>=v18'} - '@commitlint/resolve-extends@19.5.0': - resolution: {integrity: sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==} + '@commitlint/rules@19.0.3': + resolution: {integrity: sha512-TspKb9VB6svklxNCKKwxhELn7qhtY1rFF8ls58DcFd0F97XoG07xugPjjbVnLqmMkRjZDbDIwBKt9bddOfLaPw==} engines: {node: '>=v18'} - '@commitlint/rules@19.5.0': - resolution: {integrity: sha512-hDW5TPyf/h1/EufSHEKSp6Hs+YVsDMHazfJ2azIk9tHPXS6UqSz1dIRs1gpqS3eMXgtkT7JH6TW4IShdqOwhAw==} + '@commitlint/to-lines@19.0.0': + resolution: {integrity: sha512-vkxWo+VQU5wFhiP9Ub9Sre0FYe019JxFikrALVoD5UGa8/t3yOJEpEhxC5xKiENKKhUkTpEItMTRAjHw2SCpZw==} engines: {node: '>=v18'} - '@commitlint/to-lines@19.5.0': - resolution: {integrity: sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==} + '@commitlint/top-level@19.0.0': + resolution: {integrity: sha512-KKjShd6u1aMGNkCkaX4aG1jOGdn7f8ZI8TR1VEuNqUOjWTOdcDSsmglinglJ18JTjuBX5I1PtjrhQCRcixRVFQ==} engines: {node: '>=v18'} - '@commitlint/top-level@19.5.0': - resolution: {integrity: sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==} + '@commitlint/types@19.0.3': + resolution: {integrity: sha512-tpyc+7i6bPG9mvaBbtKUeghfyZSDgWquIDfMgqYtTbmZ9Y9VzEm2je9EYcQ0aoz5o7NvGS+rcDec93yO08MHYA==} engines: {node: '>=v18'} '@commitlint/types@19.5.0': @@ -2099,6 +2191,13 @@ packages: '@emotion/unitless@0.8.1': resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} + '@equinor/eds-core-react@0.41.2': + resolution: {integrity: sha512-PnPJZepi3xaF3Tah8JsRUqXGTDPjn83nigveyxTxJwVi2enkq8lPXVnEZxi7O+4CcMLopRnMIYKq8MIVPxIGVA==} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + styled-components: '>=5.1' + '@equinor/eds-core-react@0.42.0': resolution: {integrity: sha512-neu5dxT1p3wgNqAmUn4O7XtRqtQ28gVXZUzxYO5ftsOTKme08BpEdWNF2MngAlkVk8TT1hGwBbUu/OSyWFrjIQ==} peerDependencies: @@ -2515,9 +2614,6 @@ packages: '@floating-ui/core@1.6.7': resolution: {integrity: sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==} - '@floating-ui/core@1.6.8': - resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} - '@floating-ui/dom@1.6.10': resolution: {integrity: sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==} @@ -3736,11 +3832,6 @@ packages: resolution: {integrity: sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==} engines: {node: '>=14.0.0'} - '@rollup/rollup-android-arm-eabi@4.19.0': - resolution: {integrity: sha512-JlPfZ/C7yn5S5p0yKk7uhHTTnFlvTgLetl2VxqE518QgyM7C9bSfFTYvB/Q/ftkq0RIPY4ySxTz+/wKJ/dXC0w==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.21.2': resolution: {integrity: sha512-fSuPrt0ZO8uXeS+xP3b+yYTCBUd05MoSp2N/MFOgjhhUhMmchXlpTQrTpI8T+YAwAQuK7MafsCOxW7VrPMrJcg==} cpu: [arm] @@ -3751,11 +3842,6 @@ packages: cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.19.0': - resolution: {integrity: sha512-RDxUSY8D1tWYfn00DDi5myxKgOk6RvWPxhmWexcICt/MEC6yEMr4HNCu1sXXYLw8iAsg0D44NuU+qNq7zVWCrw==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.21.2': resolution: {integrity: sha512-xGU5ZQmPlsjQS6tzTTGwMsnKUtu0WVbl0hYpTPauvbRAnmIvpInhJtgjj3mcuJpEiuUw4v1s4BimkdfDWlh7gA==} cpu: [arm64] @@ -3766,11 +3852,6 @@ packages: cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.19.0': - resolution: {integrity: sha512-emvKHL4B15x6nlNTBMtIaC9tLPRpeA5jMvRLXVbl/W9Ie7HhkrE7KQjvgS9uxgatL1HmHWDXk5TTS4IaNJxbAA==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.21.2': resolution: {integrity: sha512-99AhQ3/ZMxU7jw34Sq8brzXqWH/bMnf7ZVhvLk9QU2cOepbQSVTns6qoErJmSiAvU3InRqC2RRZ5ovh1KN0d0Q==} cpu: [arm64] @@ -3781,11 +3862,6 @@ packages: cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.19.0': - resolution: {integrity: sha512-fO28cWA1dC57qCd+D0rfLC4VPbh6EOJXrreBmFLWPGI9dpMlER2YwSPZzSGfq11XgcEpPukPTfEVFtw2q2nYJg==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.21.2': resolution: {integrity: sha512-ZbRaUvw2iN/y37x6dY50D8m2BnDbBjlnMPotDi/qITMJ4sIxNY33HArjikDyakhSv0+ybdUxhWxE6kTI4oX26w==} cpu: [x64] @@ -3796,11 +3872,6 @@ packages: cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.19.0': - resolution: {integrity: sha512-2Rn36Ubxdv32NUcfm0wB1tgKqkQuft00PtM23VqLuCUR4N5jcNWDoV5iBC9jeGdgS38WK66ElncprqgMUOyomw==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-gnueabihf@4.21.2': resolution: {integrity: sha512-ztRJJMiE8nnU1YFcdbd9BcH6bGWG1z+jP+IPW2oDUAPxPjo9dverIOyXz76m6IPA6udEL12reYeLojzW2cYL7w==} cpu: [arm] @@ -3811,11 +3882,6 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.19.0': - resolution: {integrity: sha512-gJuzIVdq/X1ZA2bHeCGCISe0VWqCoNT8BvkQ+BfsixXwTOndhtLUpOg0A1Fcx/+eA6ei6rMBzlOz4JzmiDw7JQ==} - cpu: [arm] - os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.21.2': resolution: {integrity: sha512-flOcGHDZajGKYpLV0JNc0VFH361M7rnV1ee+NTeC/BQQ1/0pllYcFmxpagltANYt8FYf9+kL6RSk80Ziwyhr7w==} cpu: [arm] @@ -3826,11 +3892,6 @@ packages: cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.19.0': - resolution: {integrity: sha512-0EkX2HYPkSADo9cfeGFoQ7R0/wTKb7q6DdwI4Yn/ULFE1wuRRCHybxpl2goQrx4c/yzK3I8OlgtBu4xvted0ug==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.21.2': resolution: {integrity: sha512-69CF19Kp3TdMopyteO/LJbWufOzqqXzkrv4L2sP8kfMaAQ6iwky7NoXTp7bD6/irKgknDKM0P9E/1l5XxVQAhw==} cpu: [arm64] @@ -3841,11 +3902,6 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.19.0': - resolution: {integrity: sha512-GlIQRj9px52ISomIOEUq/IojLZqzkvRpdP3cLgIE1wUWaiU5Takwlzpz002q0Nxxr1y2ZgxC2obWxjr13lvxNQ==} - cpu: [arm64] - os: [linux] - '@rollup/rollup-linux-arm64-musl@4.21.2': resolution: {integrity: sha512-48pD/fJkTiHAZTnZwR0VzHrao70/4MlzJrq0ZsILjLW/Ab/1XlVUStYyGt7tdyIiVSlGZbnliqmult/QGA2O2w==} cpu: [arm64] @@ -3856,11 +3912,6 @@ packages: cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': - resolution: {integrity: sha512-N6cFJzssruDLUOKfEKeovCKiHcdwVYOT1Hs6dovDQ61+Y9n3Ek4zXvtghPPelt6U0AH4aDGnDLb83uiJMkWYzQ==} - cpu: [ppc64] - os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': resolution: {integrity: sha512-cZdyuInj0ofc7mAQpKcPR2a2iu4YM4FQfuUzCVA2u4HI95lCwzjoPtdWjdpDKyHxI0UO82bLDoOaLfpZ/wviyQ==} cpu: [ppc64] @@ -3871,11 +3922,6 @@ packages: cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.19.0': - resolution: {integrity: sha512-2DnD3mkS2uuam/alF+I7M84koGwvn3ZVD7uG+LEWpyzo/bq8+kKnus2EVCkcvh6PlNB8QPNFOz6fWd5N8o1CYg==} - cpu: [riscv64] - os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.21.2': resolution: {integrity: sha512-RL56JMT6NwQ0lXIQmMIWr1SW28z4E4pOhRRNqwWZeXpRlykRIlEpSWdsgNWJbYBEWD84eocjSGDu/XxbYeCmwg==} cpu: [riscv64] @@ -3886,11 +3932,6 @@ packages: cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.19.0': - resolution: {integrity: sha512-D6pkaF7OpE7lzlTOFCB2m3Ngzu2ykw40Nka9WmKGUOTS3xcIieHe82slQlNq69sVB04ch73thKYIWz/Ian8DUA==} - cpu: [s390x] - os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.21.2': resolution: {integrity: sha512-PMxkrWS9z38bCr3rWvDFVGD6sFeZJw4iQlhrup7ReGmfn7Oukrr/zweLhYX6v2/8J6Cep9IEA/SmjXjCmSbrMQ==} cpu: [s390x] @@ -3901,11 +3942,6 @@ packages: cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.19.0': - resolution: {integrity: sha512-HBndjQLP8OsdJNSxpNIN0einbDmRFg9+UQeZV1eiYupIRuZsDEoeGU43NQsS34Pp166DtwQOnpcbV/zQxM+rWA==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-gnu@4.21.2': resolution: {integrity: sha512-B90tYAUoLhU22olrafY3JQCFLnT3NglazdwkHyxNDYF/zAxJt5fJUB/yBoWFoIQ7SQj+KLe3iL4BhOMa9fzgpw==} cpu: [x64] @@ -3916,11 +3952,6 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.19.0': - resolution: {integrity: sha512-HxfbvfCKJe/RMYJJn0a12eiOI9OOtAUF4G6ozrFUK95BNyoJaSiBjIOHjZskTUffUrB84IPKkFG9H9nEvJGW6A==} - cpu: [x64] - os: [linux] - '@rollup/rollup-linux-x64-musl@4.21.2': resolution: {integrity: sha512-7twFizNXudESmC9oneLGIUmoHiiLppz/Xs5uJQ4ShvE6234K0VB1/aJYU3f/4g7PhssLGKBVCC37uRkkOi8wjg==} cpu: [x64] @@ -3931,11 +3962,6 @@ packages: cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.19.0': - resolution: {integrity: sha512-HxDMKIhmcguGTiP5TsLNolwBUK3nGGUEoV/BO9ldUBoMLBssvh4J0X8pf11i1fTV7WShWItB1bKAKjX4RQeYmg==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.21.2': resolution: {integrity: sha512-9rRero0E7qTeYf6+rFh3AErTNU1VCQg2mn7CQcI44vNUWM9Ze7MSRS/9RFuSsox+vstRt97+x3sOhEey024FRQ==} cpu: [arm64] @@ -3946,11 +3972,6 @@ packages: cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.19.0': - resolution: {integrity: sha512-xItlIAZZaiG/u0wooGzRsx11rokP4qyc/79LkAOdznGRAbOFc+SfEdfUOszG1odsHNgwippUJavag/+W/Etc6Q==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.21.2': resolution: {integrity: sha512-5rA4vjlqgrpbFVVHX3qkrCo/fZTj1q0Xxpg+Z7yIo3J2AilW7t2+n6Q8Jrx+4MrYpAnjttTYF8rr7bP46BPzRw==} cpu: [ia32] @@ -3961,11 +3982,6 @@ packages: cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.19.0': - resolution: {integrity: sha512-xNo5fV5ycvCCKqiZcpB65VMR11NJB+StnxHz20jdqRAktfdfzhgjTiJ2doTDQE/7dqGaV5I7ZGqKpgph6lCIag==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.21.2': resolution: {integrity: sha512-6UUxd0+SKomjdzuAcp+HAmxw1FlGBnl1v2yEPSabtx4lBfdXHDVsW7+lQkgz9cNFJGY3AWR7+V8P5BqkD9L9nA==} cpu: [x64] @@ -4063,9 +4079,18 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@tanstack/react-virtual@3.8.4': + resolution: {integrity: sha512-Dq0VQr3QlTS2qL35g360QaJWBt7tCn/0xw4uZ0dHXPLO1Ak4Z4nVX4vuj1Npg1b/jqNMDToRtR5OIxM2NXRBWg==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + '@tanstack/virtual-core@3.10.6': resolution: {integrity: sha512-1giLc4dzgEKLMx5pgKjL6HlG5fjZMgCjzlKAlpr7yoUtetVPELgER1NtephAI910nMwfPTHNyWKSFmJdHkz2Cw==} + '@tanstack/virtual-core@3.8.4': + resolution: {integrity: sha512-iO5Ujgw3O1yIxWDe9FgUPNkGjyT657b1WNX52u+Wv1DyBFEpdCdGkuVaky0M3hHFqNWjAmHWTn4wgj9rTr7ZQg==} + '@testing-library/dom@10.4.0': resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==} engines: {node: '>=18'} @@ -4139,9 +4164,6 @@ packages: '@types/http-errors@2.0.4': resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} - '@types/http-proxy@1.17.14': - resolution: {integrity: sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==} - '@types/is-function@1.0.3': resolution: {integrity: sha512-/CLhCW79JUeLKznI6mbVieGbl4QU5Hfn+6udw1YHZoofASjbQ5zaP5LzAUZYDpRYEjS4/P+DhEgyJ/PQmGGTWw==} @@ -4157,6 +4179,9 @@ packages: '@types/linkify-it@5.0.0': resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==} + '@types/loader-utils@2.0.6': + resolution: {integrity: sha512-cgu0Xefgq9O5FjFR78jgI6X31aPjDWCaJ6LCfRtlj6BtyVVWiXagysSYlPACwGKAzRwsFLjKXcj4iGfcVt6cLw==} + '@types/markdown-it-emoji@3.0.1': resolution: {integrity: sha512-cz1j8R35XivBqq9mwnsrP2fsz2yicLhB8+PDtuVkKOExwEdsVBNI+ROL3sbhtR5occRZ66vT0QnwFZCqdjf3pA==} @@ -4187,9 +4212,6 @@ packages: '@types/node@22.5.0': resolution: {integrity: sha512-DkFrJOe+rfdHTqqMg0bSNlGlQ85hSoh2TPzZyhHsXnMtligRWpxUySiyw8FY14ITt24HVCiQPWxS3KO/QlGmWg==} - '@types/node@22.7.4': - resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} - '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -4214,6 +4236,10 @@ packages: '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} + '@types/rollup@0.54.0': + resolution: {integrity: sha512-oeYztLHhQ98jnr+u2cs1c3tHOGtpzrm9DJlIdEjznwoXWidUbrI+X6ib7zCkPIbB7eJ7VbbKNQ5n/bPnSg6Naw==} + deprecated: This is a stub types definition for rollup (https://github.com/rollup/rollup). rollup provides its own type definitions, so you don't need @types/rollup installed! + '@types/sax@1.2.7': resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} @@ -4226,12 +4252,21 @@ packages: '@types/serve-static@1.15.7': resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/source-list-map@0.1.6': + resolution: {integrity: sha512-5JcVt1u5HDmlXkwOD2nslZVllBBc7HDuOICfiZah2Z0is8M8g+ddAEawbmd3VjedfDHBzxCaXLs07QEmb7y54g==} + '@types/stylis@4.2.5': resolution: {integrity: sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==} + '@types/tapable@1.0.12': + resolution: {integrity: sha512-bTHG8fcxEqv1M9+TD14P8ok8hjxoOCkfKc8XXLaaD05kI7ohpeI956jtDOD3XHKBQrlyPughUtzm1jtVhHpA5Q==} + '@types/trusted-types@2.0.7': resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + '@types/uglify-js@3.17.5': + resolution: {integrity: sha512-TU+fZFBTBcXj/GpDpDaBmgWk/gn96kMZ+uocaFUlV2f8a6WdMzzI44QBCmGcCiYR0Y6ZlNRiyUyKKt5nl/lbzQ==} + '@types/unist@3.0.3': resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} @@ -4247,6 +4282,12 @@ packages: '@types/webpack-env@1.18.5': resolution: {integrity: sha512-wz7kjjRRj8/Lty4B+Kr0LN6Ypc/3SymeCCGSbaXp2leH0ZVg/PriNiOwNj4bD4uphI7A8NXS4b6Gl373sfO5mA==} + '@types/webpack-sources@3.2.3': + resolution: {integrity: sha512-4nZOdMwSPHZ4pTEZzSp0AsTM4K7Qmu40UKW4tJDiOVs20UzYF9l+qUe4s0ftfN0pin06n+5cWWDJXH+sbhAiDw==} + + '@types/webpack@4.41.39': + resolution: {integrity: sha512-otxUJvoi6FbBq/64gGH34eblpKLgdi+gf08GaAh8Bx6So0ZZic028Ev/SUxD22gbthMKCkeeiXEat1kHLDJfYg==} + '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} @@ -5240,6 +5281,10 @@ packages: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + data-view-buffer@1.0.1: resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==} engines: {node: '>= 0.4'} @@ -5413,6 +5458,11 @@ packages: resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} engines: {node: '>=10'} + downshift@9.0.7: + resolution: {integrity: sha512-B6JY4sJGEl9xBEJEvC/C+QYYYtPWN5RL+TlROJQfue9+ZFgrD/p1ZgGw73I0WSTNH9/h5q3LIKQA4FLd/N10vg==} + peerDependencies: + react: '>=16.12.0' + downshift@9.0.8: resolution: {integrity: sha512-59BWD7+hSUQIM1DeNPLirNNnZIO9qMdIK5GQ/Uo8q34gT4B78RBlb9dhzgnh0HfQTJj4T/JKYD8KoLAlMWnTsA==} peerDependencies: @@ -5663,9 +5713,6 @@ packages: eventemitter3@3.1.2: resolution: {integrity: sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -5684,12 +5731,6 @@ packages: execution-time@1.4.1: resolution: {integrity: sha512-4t9svrTtsXxAEzAs9/tm1R/Voj5AYHqxd72BiLEbGQWJq2PD3tAmW8bXI7Pp0yorjaKshT1+NyKy0ytHlKW4Pg==} - express-rate-limit@7.4.0: - resolution: {integrity: sha512-v1204w3cXu5gCDmAvgvzI6qjzZzoMWKnyVDk3ACgfswTQLYiGen+r8w0VnXnGMmzEN/g8fwIQ4JrFFd4ZP6ssg==} - engines: {node: '>= 16'} - peerDependencies: - express: 4 || 5 || ^5.0.0-beta.1 - express@4.19.2: resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} engines: {node: '>= 0.10.0'} @@ -5727,6 +5768,10 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} + fetch-cookie@2.2.0: resolution: {integrity: sha512-h9AgfjURuCgA2+2ISl8GbavpUdR+WGAM2McW/ovn4tVccegp8ZqCKWSBR8uRdM8dDNlx5WdKRWxBYUwteLDCNQ==} @@ -5782,15 +5827,6 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -5798,6 +5834,10 @@ packages: resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==} engines: {node: '>=14'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -6029,19 +6069,6 @@ packages: http-post-message@0.2.3: resolution: {integrity: sha512-76heerrzYhvWptJfWxUarHw2O3fkMqF48bbq/S6XFWHUc34o8tkySBwtReXuAKJAECZWVu8U0TYLckFcwtSdrg==} - http-proxy-middleware@2.0.6: - resolution: {integrity: sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==} - engines: {node: '>=12.0.0'} - peerDependencies: - '@types/express': ^4.17.13 - peerDependenciesMeta: - '@types/express': - optional: true - - http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - human-id@1.0.2: resolution: {integrity: sha512-UNopramDEhHJD+VR+ehk8rOslwSfByxPIZyJRfV739NDhN5LF1fa1MqnzKm2lGTQRjNrjK19Q5fhkgIfjlVUKw==} @@ -6265,10 +6292,6 @@ packages: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} - is-plain-obj@3.0.0: - resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==} - engines: {node: '>=10'} - is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} @@ -6575,6 +6598,10 @@ packages: resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} engines: {node: '>=8.9.0'} + loader-utils@3.3.1: + resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} + engines: {node: '>= 12.13.0'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -6764,6 +6791,11 @@ packages: engines: {node: '>=4'} hasBin: true + mime@4.0.4: + resolution: {integrity: sha512-v8yqInVjhXyqP6+Kw4fV3ZzeMRqEW6FotRsKXjRS5VMTNIuXsdRoAvklpoRgSqXm6o9VNH4/C0mgedko9DdLsQ==} + engines: {node: '>=16'} + hasBin: true + mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} @@ -6854,6 +6886,10 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} engines: {node: 4.x || >=6.0.0} @@ -6863,6 +6899,10 @@ packages: encoding: optional: true + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-releases@2.0.18: resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} @@ -7537,11 +7577,6 @@ packages: robust-predicates@3.0.2: resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} - rollup@4.19.0: - resolution: {integrity: sha512-5r7EYSQIowHsK4eTZ0Y81qpZuJz+MUuYeqmmYmRMl1nwhdmbiYqt5jwzf6u7wyOzJgYqtCRMtVRKOtHANBz7rA==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.21.2: resolution: {integrity: sha512-e3TapAgYf9xjdLvKQCkQTnbTKd4a6jwlpQSJJFokHGaX2IVjoEqkIIhiQfqsi0cdwlOD+tQGuOd5AJkc5RngBw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -7930,9 +7965,6 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@0.3.0: - resolution: {integrity: sha512-tVGE0mVJPGb0chKhqmsoosjsS+qUnJVGJpZgsHYQcGoPlG3B51R3PouqTgEGH2Dc9jjFyOqOpix6ZHNMXp1FZg==} - tinypool@1.0.0: resolution: {integrity: sha512-KIKExllK7jp3uvrNtvRBYBWBOAXSX8ZvoaD8T+7KB/QHIuoJW3Pmr60zucywjAlMb5TeXUkcs/MWeWLu0qvuAQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -8021,38 +8053,38 @@ packages: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} - turbo-darwin-64@2.1.3: - resolution: {integrity: sha512-ouJOm0g0YyoBuhmikEujVCBGo3Zr0lbSOWFIsQtWUTItC88F2w2byhjtsYGPXQwMlTbXwmoBU2lOCfWNkeEwHQ==} + turbo-darwin-64@2.1.2: + resolution: {integrity: sha512-3TEBxHWh99h2yIzkuIigMEOXt/ItYQp0aPiJjPd1xN4oDcsKK5AxiFKPH9pdtfIBzYsY59kQhZiFj0ELnSP7Bw==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.1.3: - resolution: {integrity: sha512-j2FOJsK4LAOtHQlb3Oom0yWB/Vi0nF1ljInr311mVzHoFAJRZtfW2fRvdZRb/lBUwjSp8be58qWHzANIcrA0OA==} + turbo-darwin-arm64@2.1.2: + resolution: {integrity: sha512-he0miWNq2WxJzsH82jS2Z4MXpnkzn9SH8a79iPXiJkq25QREImucscM4RPasXm8wARp91pyysJMq6aasD45CeA==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.1.3: - resolution: {integrity: sha512-ubRHkI1gSel7H7wsmxKK8C9UlLWqg/2dkCC88LFupaK6TKgvBKqDqA0Z1M9C/escK0Jsle2k0H8bybV9OYIl4Q==} + turbo-linux-64@2.1.2: + resolution: {integrity: sha512-fKUBcc0rK8Vdqv5a/E3CSpMBLG1bzwv+Q0Q83F8fG2ZfNCNKGbcEYABdonNZkkx141Rj03cZQFCgxu3MVEGU+A==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.1.3: - resolution: {integrity: sha512-LffUL+e5wv7BtD6DgnM2kKOlDkMo2eRjhbAjVnrCD3wi2ug0tl6NDzajnHHjtaMyOnIf4AvzSKdLWsBxafGBQA==} + turbo-linux-arm64@2.1.2: + resolution: {integrity: sha512-sV8Bpmm0WiuxgbhxymcC7wSsuxfBBieI98GegSwbr/bs1ANAgzCg93urIrdKdQ3/b31zZxQwcaP4FBF1wx1Qdg==} cpu: [arm64] os: [linux] - turbo-windows-64@2.1.3: - resolution: {integrity: sha512-S9SvcZZoaq5jKr6kA6eF7/xgQhVn8Vh7PVy5lono9zybvhyL4eY++y2PaLToIgL8G9IcbLmgOC73ExNjFBg9XQ==} + turbo-windows-64@2.1.2: + resolution: {integrity: sha512-wcmIJZI9ORT9ykHGliFE6kWRQrlH930QGSjSgWC8uFChFFuOyUlvC7ttcxuSvU9VqC7NF4C+GVAcFJQ8lTjN7g==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.1.3: - resolution: {integrity: sha512-twlEo8lRrGbrR6T/ZklUIquW3IlFCEtywklgVA81aIrSBm56+GEVpSrHhIlsx1hiYeSNrs+GpDwZGe+V7fvEVQ==} + turbo-windows-arm64@2.1.2: + resolution: {integrity: sha512-zdnXjrhk7YO6CP+Q5wPueEvOCLH4lDa6C4rrwiakcWcPgcQGbVozJlo4uaQ6awo8HLWQEvOwu84RkWTdLAc/Hw==} cpu: [arm64] os: [win32] - turbo@2.1.3: - resolution: {integrity: sha512-lY0yj2GH2a2a3NExZ3rGe+rHUVeFE2aXuRAue57n+08E7Z7N7YCmynju0kPC1grAQzERmoLpKrmzmWd+PNiADw==} + turbo@2.1.2: + resolution: {integrity: sha512-Jb0rbU4iHEVQ18An/YfakdIv9rKnd3zUfSE117EngrfWXFHo3RndVH96US3GsT8VHpwTncPePDBT2t06PaFLrw==} hasBin: true type-check@0.4.0: @@ -8314,49 +8346,18 @@ packages: terser: optional: true - vite@5.4.8: - resolution: {integrity: sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==} + vitest-github-actions-reporter@0.11.1: + resolution: {integrity: sha512-ZHHB0wBgOPhMYCB17WKVlJZa+5SdudBZFoVoebwfq3ioIUTeLQGYHwh85vpdJAxRghLP8d0qI/6eCTueGyDVXA==} + engines: {node: '>=14.16.0'} + peerDependencies: + vitest: '>=0.28.5' + + vitest@2.0.5: + resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: - '@types/node': ^18.0.0 || >=20.0.0 - less: '*' - lightningcss: ^1.21.0 - sass: '*' - sass-embedded: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - - vitest-github-actions-reporter@0.11.1: - resolution: {integrity: sha512-ZHHB0wBgOPhMYCB17WKVlJZa+5SdudBZFoVoebwfq3ioIUTeLQGYHwh85vpdJAxRghLP8d0qI/6eCTueGyDVXA==} - engines: {node: '>=14.16.0'} - peerDependencies: - vitest: '>=0.28.5' - - vitest@2.0.5: - resolution: {integrity: sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==} - engines: {node: ^18.0.0 || >=20.0.0} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' + '@edge-runtime/vm': '*' '@types/node': ^18.0.0 || >=20.0.0 '@vitest/browser': 2.0.5 '@vitest/ui': 2.0.5 @@ -8561,6 +8562,10 @@ packages: wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -8717,9 +8722,9 @@ snapshots: tunnel: 0.0.6 undici: 5.28.4 - '@ag-grid-community/client-side-row-model@32.2.0': + '@ag-grid-community/client-side-row-model@32.2.1': dependencies: - '@ag-grid-community/core': 32.2.0 + '@ag-grid-community/core': 32.2.1 tslib: 2.7.0 '@ag-grid-community/core@32.0.2': @@ -8727,7 +8732,7 @@ snapshots: ag-charts-types: 10.0.2 tslib: 2.7.0 - '@ag-grid-community/core@32.2.0': + '@ag-grid-community/core@32.2.1': dependencies: ag-charts-types: 10.2.0 tslib: 2.7.0 @@ -8737,9 +8742,9 @@ snapshots: '@ag-grid-community/core': 32.0.2 tslib: 2.7.0 - '@ag-grid-community/react@32.2.0(@ag-grid-community/core@32.2.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@ag-grid-community/react@32.2.1(@ag-grid-community/core@32.2.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: - '@ag-grid-community/core': 32.2.0 + '@ag-grid-community/core': 32.2.1 prop-types: 15.8.1 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -8763,9 +8768,9 @@ snapshots: dependencies: '@ag-grid-community/core': 32.0.2 - '@ag-grid-enterprise/core@32.2.0': + '@ag-grid-enterprise/core@32.2.1': dependencies: - '@ag-grid-community/core': 32.2.0 + '@ag-grid-community/core': 32.2.1 '@ag-grid-enterprise/excel-export@32.0.2': dependencies: @@ -8826,10 +8831,10 @@ snapshots: '@babel/helper-compilation-targets': 7.24.8 '@babel/helper-module-transforms': 7.24.9(@babel/core@7.24.9) '@babel/helpers': 7.24.8 - '@babel/parser': 7.24.8 + '@babel/parser': 7.25.4 '@babel/template': 7.24.7 '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 convert-source-map: 2.0.0 debug: 4.3.6 gensync: 1.0.0-beta.2 @@ -8840,7 +8845,7 @@ snapshots: '@babel/generator@7.24.10': dependencies: - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -8906,7 +8911,7 @@ snapshots: '@babel/helpers@7.24.8': dependencies: '@babel/template': 7.24.7 - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 '@babel/highlight@7.24.7': dependencies: @@ -8937,7 +8942,7 @@ snapshots: dependencies: regenerator-runtime: 0.14.1 - '@babel/runtime@7.25.4': + '@babel/runtime@7.25.0': dependencies: regenerator-runtime: 0.14.1 @@ -8949,7 +8954,7 @@ snapshots: dependencies: '@babel/code-frame': 7.24.7 '@babel/parser': 7.25.4 - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 '@babel/traverse@7.24.8': dependencies: @@ -8960,7 +8965,7 @@ snapshots: '@babel/helper-hoist-variables': 7.24.7 '@babel/helper-split-export-declaration': 7.24.7 '@babel/parser': 7.25.4 - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 debug: 4.3.6 globals: 11.12.0 transitivePeerDependencies: @@ -9158,14 +9163,14 @@ snapshots: '@chevrotain/utils@11.0.3': {} - '@commitlint/cli@19.5.0(@types/node@22.7.4)(typescript@5.6.2)': + '@commitlint/cli@19.4.0(@types/node@22.5.0)(typescript@5.6.2)': dependencies: - '@commitlint/format': 19.5.0 - '@commitlint/lint': 19.5.0 - '@commitlint/load': 19.5.0(@types/node@22.7.4)(typescript@5.6.2) - '@commitlint/read': 19.5.0 - '@commitlint/types': 19.5.0 - tinyexec: 0.3.0 + '@commitlint/format': 19.3.0 + '@commitlint/lint': 19.2.2 + '@commitlint/load': 19.4.0(@types/node@22.5.0)(typescript@5.6.2) + '@commitlint/read': 19.4.0 + '@commitlint/types': 19.0.3 + execa: 8.0.1 yargs: 17.7.2 transitivePeerDependencies: - '@types/node' @@ -9176,12 +9181,12 @@ snapshots: '@commitlint/types': 19.5.0 conventional-changelog-conventionalcommits: 7.0.2 - '@commitlint/config-validator@19.5.0': + '@commitlint/config-validator@19.0.3': dependencies: '@commitlint/types': 19.5.0 ajv: 8.17.1 - '@commitlint/ensure@19.5.0': + '@commitlint/ensure@19.0.3': dependencies: '@commitlint/types': 19.5.0 lodash.camelcase: 4.3.0 @@ -9190,34 +9195,34 @@ snapshots: lodash.startcase: 4.4.0 lodash.upperfirst: 4.3.1 - '@commitlint/execute-rule@19.5.0': {} + '@commitlint/execute-rule@19.0.0': {} - '@commitlint/format@19.5.0': + '@commitlint/format@19.3.0': dependencies: - '@commitlint/types': 19.5.0 + '@commitlint/types': 19.0.3 chalk: 5.3.0 - '@commitlint/is-ignored@19.5.0': + '@commitlint/is-ignored@19.2.2': dependencies: '@commitlint/types': 19.5.0 semver: 7.6.3 - '@commitlint/lint@19.5.0': + '@commitlint/lint@19.2.2': dependencies: - '@commitlint/is-ignored': 19.5.0 - '@commitlint/parse': 19.5.0 - '@commitlint/rules': 19.5.0 - '@commitlint/types': 19.5.0 + '@commitlint/is-ignored': 19.2.2 + '@commitlint/parse': 19.0.3 + '@commitlint/rules': 19.0.3 + '@commitlint/types': 19.0.3 - '@commitlint/load@19.5.0(@types/node@22.7.4)(typescript@5.6.2)': + '@commitlint/load@19.4.0(@types/node@22.5.0)(typescript@5.6.2)': dependencies: - '@commitlint/config-validator': 19.5.0 - '@commitlint/execute-rule': 19.5.0 - '@commitlint/resolve-extends': 19.5.0 - '@commitlint/types': 19.5.0 + '@commitlint/config-validator': 19.0.3 + '@commitlint/execute-rule': 19.0.0 + '@commitlint/resolve-extends': 19.1.0 + '@commitlint/types': 19.0.3 chalk: 5.3.0 cosmiconfig: 9.0.0(typescript@5.6.2) - cosmiconfig-typescript-loader: 5.0.0(@types/node@22.7.4)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2) + cosmiconfig-typescript-loader: 5.0.0(@types/node@22.5.0)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 @@ -9225,44 +9230,50 @@ snapshots: - '@types/node' - typescript - '@commitlint/message@19.5.0': {} + '@commitlint/message@19.0.0': {} - '@commitlint/parse@19.5.0': + '@commitlint/parse@19.0.3': dependencies: '@commitlint/types': 19.5.0 conventional-changelog-angular: 7.0.0 conventional-commits-parser: 5.0.0 - '@commitlint/read@19.5.0': + '@commitlint/read@19.4.0': dependencies: - '@commitlint/top-level': 19.5.0 - '@commitlint/types': 19.5.0 + '@commitlint/top-level': 19.0.0 + '@commitlint/types': 19.0.3 + execa: 8.0.1 git-raw-commits: 4.0.0 minimist: 1.2.8 - tinyexec: 0.3.0 - '@commitlint/resolve-extends@19.5.0': + '@commitlint/resolve-extends@19.1.0': dependencies: - '@commitlint/config-validator': 19.5.0 + '@commitlint/config-validator': 19.0.3 '@commitlint/types': 19.5.0 global-directory: 4.0.1 import-meta-resolve: 4.1.0 lodash.mergewith: 4.6.2 resolve-from: 5.0.0 - '@commitlint/rules@19.5.0': + '@commitlint/rules@19.0.3': dependencies: - '@commitlint/ensure': 19.5.0 - '@commitlint/message': 19.5.0 - '@commitlint/to-lines': 19.5.0 + '@commitlint/ensure': 19.0.3 + '@commitlint/message': 19.0.0 + '@commitlint/to-lines': 19.0.0 '@commitlint/types': 19.5.0 + execa: 8.0.1 - '@commitlint/to-lines@19.5.0': {} + '@commitlint/to-lines@19.0.0': {} - '@commitlint/top-level@19.5.0': + '@commitlint/top-level@19.0.0': dependencies: find-up: 7.0.0 + '@commitlint/types@19.0.3': + dependencies: + '@types/conventional-commits-parser': 5.0.0 + chalk: 5.3.0 + '@commitlint/types@19.5.0': dependencies: '@types/conventional-commits-parser': 5.0.0 @@ -9278,6 +9289,25 @@ snapshots: '@emotion/unitless@0.8.1': {} + '@equinor/eds-core-react@0.41.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + dependencies: + '@babel/runtime': 7.25.0 + '@equinor/eds-icons': 0.21.0 + '@equinor/eds-tokens': 0.9.2 + '@equinor/eds-utils': 0.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@floating-ui/react': 0.26.23(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@internationalized/date': 3.5.5 + '@react-aria/utils': 3.25.1(react@18.3.1) + '@react-stately/calendar': 3.5.3(react@18.3.1) + '@react-stately/datepicker': 3.10.1(react@18.3.1) + '@react-types/shared': 3.24.1(react@18.3.1) + '@tanstack/react-virtual': 3.8.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + downshift: 9.0.7(react@18.3.1) + react: 18.3.1 + react-aria: 3.34.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-dom: 18.3.1(react@18.3.1) + styled-components: 6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@equinor/eds-core-react@0.42.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: '@babel/runtime': 7.25.6 @@ -9324,7 +9354,7 @@ snapshots: '@equinor/eds-utils@0.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@equinor/eds-tokens': 0.9.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) @@ -9332,12 +9362,21 @@ snapshots: '@equinor/eds-utils@0.8.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: - '@babel/runtime': 7.25.4 + '@babel/runtime': 7.25.6 '@equinor/eds-tokens': 0.9.2 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) styled-components: 6.1.13(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@equinor/eslint-config-fusion-react@2.1.1(@equinor/eslint-config-fusion@2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.5.4))(eslint@8.57.1)': + dependencies: + '@equinor/eslint-config-fusion': 2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.5.4) + eslint-plugin-react: 7.35.0(eslint@8.57.1) + eslint-plugin-react-hooks: 4.6.2(eslint@8.57.1) + eslint-plugin-styled-components-a11y: 2.1.35(eslint@8.57.1) + transitivePeerDependencies: + - eslint + '@equinor/eslint-config-fusion-react@2.1.1(@equinor/eslint-config-fusion@2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.6.2))(eslint@8.57.1)': dependencies: '@equinor/eslint-config-fusion': 2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.6.2) @@ -9347,6 +9386,19 @@ snapshots: transitivePeerDependencies: - eslint + '@equinor/eslint-config-fusion@2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@8.57.1)(typescript@5.5.4) + eslint: 8.57.1 + eslint-config-prettier: 9.1.0(eslint@8.57.1) + eslint-plugin-prettier: 5.2.1(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.3.3) + prettier: 3.3.3 + typescript: 5.5.4 + transitivePeerDependencies: + - '@types/eslint' + - supports-color + '@equinor/eslint-config-fusion@2.3.0(eslint@8.57.1)(prettier@3.3.3)(typescript@5.6.2)': dependencies: '@typescript-eslint/eslint-plugin': 7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) @@ -9470,9 +9522,9 @@ snapshots: transitivePeerDependencies: - react - '@equinor/fusion-react-side-sheet@1.3.3(@equinor/eds-core-react@0.42.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@equinor/eds-icons@0.21.0)(@equinor/eds-tokens@0.9.2)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': + '@equinor/fusion-react-side-sheet@1.3.3(@equinor/eds-core-react@0.41.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)))(@equinor/eds-icons@0.21.0)(@equinor/eds-tokens@0.9.2)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1))': dependencies: - '@equinor/eds-core-react': 0.42.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + '@equinor/eds-core-react': 0.41.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(styled-components@6.1.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) '@equinor/eds-icons': 0.21.0 '@equinor/eds-tokens': 0.9.2 re-resizable: 6.9.17(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -9912,10 +9964,6 @@ snapshots: dependencies: '@floating-ui/utils': 0.2.7 - '@floating-ui/core@1.6.8': - dependencies: - '@floating-ui/utils': 0.2.8 - '@floating-ui/dom@1.6.10': dependencies: '@floating-ui/core': 1.6.7 @@ -9923,7 +9971,7 @@ snapshots: '@floating-ui/dom@1.6.11': dependencies: - '@floating-ui/core': 1.6.8 + '@floating-ui/core': 1.6.7 '@floating-ui/utils': 0.2.8 '@floating-ui/react-dom@2.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -11880,144 +11928,96 @@ snapshots: '@remix-run/router@1.18.0': {} - '@rollup/rollup-android-arm-eabi@4.19.0': - optional: true - '@rollup/rollup-android-arm-eabi@4.21.2': optional: true '@rollup/rollup-android-arm-eabi@4.22.4': optional: true - '@rollup/rollup-android-arm64@4.19.0': - optional: true - '@rollup/rollup-android-arm64@4.21.2': optional: true '@rollup/rollup-android-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-arm64@4.19.0': - optional: true - '@rollup/rollup-darwin-arm64@4.21.2': optional: true '@rollup/rollup-darwin-arm64@4.22.4': optional: true - '@rollup/rollup-darwin-x64@4.19.0': - optional: true - '@rollup/rollup-darwin-x64@4.21.2': optional: true '@rollup/rollup-darwin-x64@4.22.4': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.19.0': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.21.2': optional: true '@rollup/rollup-linux-arm-gnueabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.19.0': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.21.2': optional: true '@rollup/rollup-linux-arm-musleabihf@4.22.4': optional: true - '@rollup/rollup-linux-arm64-gnu@4.19.0': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.21.2': optional: true '@rollup/rollup-linux-arm64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-arm64-musl@4.19.0': - optional: true - '@rollup/rollup-linux-arm64-musl@4.21.2': optional: true '@rollup/rollup-linux-arm64-musl@4.22.4': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.19.0': - optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.21.2': optional: true '@rollup/rollup-linux-powerpc64le-gnu@4.22.4': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.19.0': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.21.2': optional: true '@rollup/rollup-linux-riscv64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-s390x-gnu@4.19.0': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.21.2': optional: true '@rollup/rollup-linux-s390x-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-gnu@4.19.0': - optional: true - '@rollup/rollup-linux-x64-gnu@4.21.2': optional: true '@rollup/rollup-linux-x64-gnu@4.22.4': optional: true - '@rollup/rollup-linux-x64-musl@4.19.0': - optional: true - '@rollup/rollup-linux-x64-musl@4.21.2': optional: true '@rollup/rollup-linux-x64-musl@4.22.4': optional: true - '@rollup/rollup-win32-arm64-msvc@4.19.0': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.21.2': optional: true '@rollup/rollup-win32-arm64-msvc@4.22.4': optional: true - '@rollup/rollup-win32-ia32-msvc@4.19.0': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.21.2': optional: true '@rollup/rollup-win32-ia32-msvc@4.22.4': optional: true - '@rollup/rollup-win32-x64-msvc@4.19.0': - optional: true - '@rollup/rollup-win32-x64-msvc@4.21.2': optional: true @@ -12161,8 +12161,16 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@tanstack/react-virtual@3.8.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@tanstack/virtual-core': 3.8.4 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + '@tanstack/virtual-core@3.10.6': {} + '@tanstack/virtual-core@3.8.4': {} + '@testing-library/dom@10.4.0': dependencies: '@babel/code-frame': 7.24.7 @@ -12259,22 +12267,23 @@ snapshots: '@types/http-errors@2.0.4': {} - '@types/http-proxy@1.17.14': - dependencies: - '@types/node': 20.14.12 - '@types/is-function@1.0.3': {} '@types/json-schema@7.0.15': {} '@types/jsonfile@6.1.4': dependencies: - '@types/node': 22.5.0 + '@types/node': 20.14.12 '@types/katex@0.16.7': {} '@types/linkify-it@5.0.0': {} + '@types/loader-utils@2.0.6': + dependencies: + '@types/node': 20.14.12 + '@types/webpack': 4.41.39 + '@types/markdown-it-emoji@3.0.1': dependencies: '@types/markdown-it': 14.1.2 @@ -12304,10 +12313,6 @@ snapshots: dependencies: undici-types: 6.19.8 - '@types/node@22.7.4': - dependencies: - undici-types: 6.19.8 - '@types/normalize-package-data@2.4.4': {} '@types/prop-types@15.7.12': {} @@ -12336,6 +12341,10 @@ snapshots: '@types/prop-types': 15.7.12 csstype: 3.1.3 + '@types/rollup@0.54.0': + dependencies: + rollup: 4.22.4 + '@types/sax@1.2.7': dependencies: '@types/node': 20.14.12 @@ -12353,10 +12362,18 @@ snapshots: '@types/node': 20.14.12 '@types/send': 0.17.4 + '@types/source-list-map@0.1.6': {} + '@types/stylis@4.2.5': {} + '@types/tapable@1.0.12': {} + '@types/trusted-types@2.0.7': {} + '@types/uglify-js@3.17.5': + dependencies: + source-map: 0.6.1 + '@types/unist@3.0.3': {} '@types/uuid@10.0.0': {} @@ -12367,12 +12384,45 @@ snapshots: '@types/webpack-env@1.18.5': {} + '@types/webpack-sources@3.2.3': + dependencies: + '@types/node': 20.14.12 + '@types/source-list-map': 0.1.6 + source-map: 0.7.4 + + '@types/webpack@4.41.39': + dependencies: + '@types/node': 20.14.12 + '@types/tapable': 1.0.12 + '@types/uglify-js': 3.17.5 + '@types/webpack-sources': 3.2.3 + anymatch: 3.1.3 + source-map: 0.6.1 + '@types/yargs-parser@21.0.3': {} '@types/yargs@17.0.32': dependencies: '@types/yargs-parser': 21.0.3 + '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.5.4))(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@eslint-community/regexpp': 4.11.0 + '@typescript-eslint/parser': 7.17.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/type-utils': 7.17.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@8.57.1)(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + eslint: 8.57.1 + graphemer: 1.4.0 + ignore: 5.3.1 + natural-compare: 1.4.0 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/eslint-plugin@7.17.0(@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.11.0 @@ -12391,6 +12441,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.5.4) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/experimental-utils@5.62.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@typescript-eslint/utils': 5.62.0(eslint@8.57.1)(typescript@5.6.2) @@ -12399,6 +12457,19 @@ snapshots: - supports-color - typescript + '@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.6 + eslint: 8.57.1 + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/parser@7.17.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 7.17.0 @@ -12422,6 +12493,18 @@ snapshots: '@typescript-eslint/types': 7.17.0 '@typescript-eslint/visitor-keys': 7.17.0 + '@typescript-eslint/type-utils@7.17.0(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + '@typescript-eslint/utils': 7.17.0(eslint@8.57.1)(typescript@5.5.4) + debug: 4.3.6 + eslint: 8.57.1 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/type-utils@7.17.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.6.2) @@ -12438,6 +12521,20 @@ snapshots: '@typescript-eslint/types@7.17.0': {} + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/visitor-keys': 5.62.0 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.6.3 + tsutils: 3.21.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@5.62.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 5.62.0 @@ -12452,6 +12549,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@7.17.0(typescript@5.5.4)': + dependencies: + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/visitor-keys': 7.17.0 + debug: 4.3.6 + globby: 11.1.0 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.6.3 + ts-api-utils: 1.3.0(typescript@5.5.4) + optionalDependencies: + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/typescript-estree@7.17.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 7.17.0 @@ -12467,6 +12579,21 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@types/json-schema': 7.0.15 + '@types/semver': 7.5.8 + '@typescript-eslint/scope-manager': 5.62.0 + '@typescript-eslint/types': 5.62.0 + '@typescript-eslint/typescript-estree': 5.62.0(typescript@5.5.4) + eslint: 8.57.1 + eslint-scope: 5.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/utils@5.62.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) @@ -12482,6 +12609,17 @@ snapshots: - supports-color - typescript + '@typescript-eslint/utils@7.17.0(eslint@8.57.1)(typescript@5.5.4)': + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@typescript-eslint/scope-manager': 7.17.0 + '@typescript-eslint/types': 7.17.0 + '@typescript-eslint/typescript-estree': 7.17.0(typescript@5.5.4) + eslint: 8.57.1 + transitivePeerDependencies: + - supports-color + - typescript + '@typescript-eslint/utils@7.17.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) @@ -12516,12 +12654,23 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitejs/plugin-vue@5.1.2(vite@5.4.6(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8))(vue@3.4.38(typescript@5.5.4))': + '@vitejs/plugin-react@4.3.1(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8))': dependencies: - vite: 5.4.6(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8) + '@babel/core': 7.24.9 + '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) + '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) + '@types/babel__core': 7.20.5 + react-refresh: 0.14.2 + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) + transitivePeerDependencies: + - supports-color + + '@vitejs/plugin-vue@5.1.2(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8))(vue@3.4.38(typescript@5.5.4))': + dependencies: + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) vue: 3.4.38(typescript@5.5.4) - '@vitest/coverage-v8@2.0.5(vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8))': + '@vitest/coverage-v8@2.0.5(vitest@2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8))': dependencies: '@ampproject/remapping': 2.3.0 '@bcoe/v8-coverage': 0.2.3 @@ -12535,7 +12684,7 @@ snapshots: std-env: 3.7.0 test-exclude: 7.0.1 tinyrainbow: 1.2.0 - vitest: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) + vitest: 2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) transitivePeerDependencies: - supports-color @@ -12628,9 +12777,9 @@ snapshots: '@vue/shared@3.4.38': {} - '@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0)': + '@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0)': dependencies: - '@vitejs/plugin-vue': 5.1.2(vite@5.4.6(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8))(vue@3.4.38(typescript@5.5.4)) + '@vitejs/plugin-vue': 5.1.2(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8))(vue@3.4.38(typescript@5.5.4)) '@vuepress/client': 2.0.0-rc.15(typescript@5.5.4) '@vuepress/core': 2.0.0-rc.15(typescript@5.5.4) '@vuepress/shared': 2.0.0-rc.15 @@ -12639,8 +12788,8 @@ snapshots: connect-history-api-fallback: 2.0.0 postcss: 8.4.41 postcss-load-config: 6.0.1(jiti@1.21.6)(postcss@8.4.41)(yaml@2.5.0) - rollup: 4.21.2 - vite: 5.4.6(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8) + rollup: 4.22.4 + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) vue: 3.4.38(typescript@5.5.4) vue-router: 4.4.3(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: @@ -12691,20 +12840,20 @@ snapshots: - supports-color - typescript - '@vuepress/helper@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/helper@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: '@vue/shared': 3.4.38 cheerio: 1.0.0-rc.12 fflate: 0.8.2 gray-matter: 4.0.3 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/highlighter-helper@2.0.0-rc.39(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/highlighter-helper@2.0.0-rc.39(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) '@vuepress/markdown@2.0.0-rc.15': dependencies: @@ -12727,179 +12876,179 @@ snapshots: transitivePeerDependencies: - supports-color - '@vuepress/plugin-active-header-links@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-active-header-links@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-back-to-top@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-back-to-top@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-blog@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-blog@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) chokidar: 3.6.0 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-catalog@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-catalog@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-comment@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-comment@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) giscus: 1.5.0 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-copy-code@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-copy-code@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-copyright@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-copyright@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-git@2.0.0-rc.38(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-git@2.0.0-rc.38(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: execa: 9.3.1 - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) - '@vuepress/plugin-links-check@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-links-check@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-notice@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-notice@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-nprogress@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-nprogress@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-photo-swipe@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-photo-swipe@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) photoswipe: 5.4.4 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - '@vuepress/plugin-reading-time@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-reading-time@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-register-components@2.0.0-rc.44(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-register-components@2.0.0-rc.44(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: chokidar: 3.6.0 - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) - '@vuepress/plugin-rtl@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-rtl@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-sass-palette@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-sass-palette@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) chokidar: 3.6.0 sass: 1.77.8 - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-seo@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-seo@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-shiki@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-shiki@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: '@shikijs/transformers': 1.14.1 - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/highlighter-helper': 2.0.0-rc.39(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/highlighter-helper': 2.0.0-rc.39(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) nanoid: 5.0.7 shiki: 1.14.1 - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-sitemap@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-sitemap@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) sitemap: 8.0.0 - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-theme-data@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-theme-data@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: '@vue/devtools-api': 6.6.3 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - typescript - '@vuepress/plugin-watermark@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': + '@vuepress/plugin-watermark@2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)))': dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) watermark-js-plus: 1.5.3 transitivePeerDependencies: - typescript @@ -13421,9 +13570,9 @@ snapshots: dependencies: layout-base: 1.0.2 - cosmiconfig-typescript-loader@5.0.0(@types/node@22.7.4)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2): + cosmiconfig-typescript-loader@5.0.0(@types/node@22.5.0)(cosmiconfig@9.0.0(typescript@5.6.2))(typescript@5.6.2): dependencies: - '@types/node': 22.7.4 + '@types/node': 22.5.0 cosmiconfig: 9.0.0(typescript@5.6.2) jiti: 1.21.6 typescript: 5.6.2 @@ -13674,6 +13823,8 @@ snapshots: dargs@8.1.0: {} + data-uri-to-buffer@4.0.1: {} + data-view-buffer@1.0.1: dependencies: call-bind: 1.0.7 @@ -13834,6 +13985,15 @@ snapshots: dotenv@8.6.0: {} + downshift@9.0.7(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.6 + compute-scroll-into-view: 3.1.0 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.2.0 + tslib: 2.7.0 + downshift@9.0.8(react@18.3.1): dependencies: '@babel/runtime': 7.25.6 @@ -14034,6 +14194,16 @@ snapshots: dependencies: eslint: 8.57.1 + eslint-etc@5.2.1(eslint@8.57.1)(typescript@5.5.4): + dependencies: + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.5.4) + eslint: 8.57.1 + tsutils: 3.21.0(typescript@5.5.4) + tsutils-etc: 1.4.2(tsutils@3.21.0(typescript@5.5.4))(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + eslint-etc@5.2.1(eslint@8.57.1)(typescript@5.6.2): dependencies: '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.6.2) @@ -14101,6 +14271,22 @@ snapshots: string.prototype.matchall: 4.0.11 string.prototype.repeat: 1.0.0 + eslint-plugin-rxjs@5.0.3(eslint@8.57.1)(typescript@5.5.4): + dependencies: + '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.5.4) + common-tags: 1.8.2 + decamelize: 5.0.1 + eslint: 8.57.1 + eslint-etc: 5.2.1(eslint@8.57.1)(typescript@5.5.4) + requireindex: 1.2.0 + rxjs-report-usage: 1.0.6 + tslib: 2.7.0 + tsutils: 3.21.0(typescript@5.5.4) + tsutils-etc: 1.4.2(tsutils@3.21.0(typescript@5.5.4))(typescript@5.5.4) + typescript: 5.5.4 + transitivePeerDependencies: + - supports-color + eslint-plugin-rxjs@5.0.3(eslint@8.57.1)(typescript@5.6.2): dependencies: '@typescript-eslint/experimental-utils': 5.62.0(eslint@8.57.1)(typescript@5.6.2) @@ -14119,7 +14305,7 @@ snapshots: eslint-plugin-styled-components-a11y@2.1.35(eslint@8.57.1): dependencies: - '@babel/parser': 7.24.8 + '@babel/parser': 7.25.4 eslint: 8.57.1 eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.1) @@ -14212,8 +14398,6 @@ snapshots: eventemitter3@3.1.2: {} - eventemitter3@4.0.7: {} - eventemitter3@5.0.1: {} eventsource@2.0.2: {} @@ -14249,10 +14433,6 @@ snapshots: dependencies: pretty-hrtime: 1.0.3 - express-rate-limit@7.4.0(express@4.19.2): - dependencies: - express: 4.19.2 - express@4.19.2: dependencies: accepts: 1.3.8 @@ -14323,6 +14503,11 @@ snapshots: dependencies: reusify: 1.0.4 + fetch-blob@3.2.0: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 + fetch-cookie@2.2.0: dependencies: set-cookie-parser: 2.6.0 @@ -14392,8 +14577,6 @@ snapshots: flatted@3.3.1: {} - follow-redirects@1.15.6: {} - for-each@0.3.3: dependencies: is-callable: 1.2.7 @@ -14403,6 +14586,10 @@ snapshots: cross-spawn: 7.0.3 signal-exit: 4.1.0 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + forwarded@0.2.0: {} fraction.js@4.3.7: {} @@ -14668,26 +14855,6 @@ snapshots: dependencies: es6-promise: 3.3.1 - http-proxy-middleware@2.0.6(@types/express@4.17.21): - dependencies: - '@types/http-proxy': 1.17.14 - http-proxy: 1.18.1 - is-glob: 4.0.3 - is-plain-obj: 3.0.0 - micromatch: 4.0.7 - optionalDependencies: - '@types/express': 4.17.21 - transitivePeerDependencies: - - debug - - http-proxy@1.18.1: - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.6 - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - human-id@1.0.2: {} human-signals@5.0.0: {} @@ -14881,8 +15048,6 @@ snapshots: is-plain-obj@1.1.0: {} - is-plain-obj@3.0.0: {} - is-plain-obj@4.1.0: {} is-regex@1.1.4: @@ -15248,6 +15413,8 @@ snapshots: emojis-list: 3.0.0 json5: 2.2.3 + loader-utils@3.3.1: {} + locate-path@5.0.0: dependencies: p-locate: 4.1.0 @@ -15444,6 +15611,8 @@ snapshots: mime@1.6.0: {} + mime@4.0.4: {} + mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} @@ -15509,10 +15678,18 @@ snapshots: negotiator@0.6.3: {} + node-domexception@1.0.0: {} + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-releases@2.0.18: {} normalize-package-data@2.5.0: @@ -16343,28 +16520,6 @@ snapshots: robust-predicates@3.0.2: {} - rollup@4.19.0: - dependencies: - '@types/estree': 1.0.5 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.19.0 - '@rollup/rollup-android-arm64': 4.19.0 - '@rollup/rollup-darwin-arm64': 4.19.0 - '@rollup/rollup-darwin-x64': 4.19.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.19.0 - '@rollup/rollup-linux-arm-musleabihf': 4.19.0 - '@rollup/rollup-linux-arm64-gnu': 4.19.0 - '@rollup/rollup-linux-arm64-musl': 4.19.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.19.0 - '@rollup/rollup-linux-riscv64-gnu': 4.19.0 - '@rollup/rollup-linux-s390x-gnu': 4.19.0 - '@rollup/rollup-linux-x64-gnu': 4.19.0 - '@rollup/rollup-linux-x64-musl': 4.19.0 - '@rollup/rollup-win32-arm64-msvc': 4.19.0 - '@rollup/rollup-win32-ia32-msvc': 4.19.0 - '@rollup/rollup-win32-x64-msvc': 4.19.0 - fsevents: 2.3.3 - rollup@4.21.2: dependencies: '@types/estree': 1.0.5 @@ -16430,9 +16585,9 @@ snapshots: rxjs-report-usage@1.0.6: dependencies: - '@babel/parser': 7.24.8 + '@babel/parser': 7.25.4 '@babel/traverse': 7.24.8 - '@babel/types': 7.24.9 + '@babel/types': 7.25.4 bent: 7.3.12 chalk: 4.1.2 glob: 7.2.3 @@ -16618,8 +16773,7 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.4: - optional: true + source-map@0.7.4: {} spawndamnit@2.0.0: dependencies: @@ -16844,8 +16998,6 @@ snapshots: tinybench@2.9.0: {} - tinyexec@0.3.0: {} - tinypool@1.0.0: {} tinyrainbow@1.2.0: {} @@ -16875,6 +17027,10 @@ snapshots: trim-newlines@3.0.1: {} + ts-api-utils@1.3.0(typescript@5.5.4): + dependencies: + typescript: 5.5.4 + ts-api-utils@1.3.0(typescript@5.6.2): dependencies: typescript: 5.6.2 @@ -16893,6 +17049,13 @@ snapshots: tslib@2.7.0: {} + tsutils-etc@1.4.2(tsutils@3.21.0(typescript@5.5.4))(typescript@5.5.4): + dependencies: + '@types/yargs': 17.0.32 + tsutils: 3.21.0(typescript@5.5.4) + typescript: 5.5.4 + yargs: 17.7.2 + tsutils-etc@1.4.2(tsutils@3.21.0(typescript@5.6.2))(typescript@5.6.2): dependencies: '@types/yargs': 17.0.32 @@ -16900,6 +17063,11 @@ snapshots: typescript: 5.6.2 yargs: 17.7.2 + tsutils@3.21.0(typescript@5.5.4): + dependencies: + tslib: 1.14.1 + typescript: 5.5.4 + tsutils@3.21.0(typescript@5.6.2): dependencies: tslib: 1.14.1 @@ -16907,32 +17075,32 @@ snapshots: tunnel@0.0.6: {} - turbo-darwin-64@2.1.3: + turbo-darwin-64@2.1.2: optional: true - turbo-darwin-arm64@2.1.3: + turbo-darwin-arm64@2.1.2: optional: true - turbo-linux-64@2.1.3: + turbo-linux-64@2.1.2: optional: true - turbo-linux-arm64@2.1.3: + turbo-linux-arm64@2.1.2: optional: true - turbo-windows-64@2.1.3: + turbo-windows-64@2.1.2: optional: true - turbo-windows-arm64@2.1.3: + turbo-windows-arm64@2.1.2: optional: true - turbo@2.1.3: + turbo@2.1.2: optionalDependencies: - turbo-darwin-64: 2.1.3 - turbo-darwin-arm64: 2.1.3 - turbo-linux-64: 2.1.3 - turbo-linux-arm64: 2.1.3 - turbo-windows-64: 2.1.3 - turbo-windows-arm64: 2.1.3 + turbo-darwin-64: 2.1.2 + turbo-darwin-arm64: 2.1.2 + turbo-linux-64: 2.1.2 + turbo-linux-arm64: 2.1.2 + turbo-windows-64: 2.1.2 + turbo-windows-arm64: 2.1.2 type-check@0.4.0: dependencies: @@ -17093,7 +17261,7 @@ snapshots: debug: 4.3.6 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8) + vite: 5.4.6(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8) transitivePeerDependencies: - '@types/node' - less @@ -17105,13 +17273,13 @@ snapshots: - supports-color - terser - vite-node@2.0.5(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8): + vite-node@2.0.5(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8): dependencies: cac: 6.7.14 debug: 4.3.6 pathe: 1.1.2 tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8) + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) transitivePeerDependencies: - '@types/node' - less @@ -17127,6 +17295,10 @@ snapshots: dependencies: vite: 5.4.3(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8) + vite-plugin-environment@1.1.3(vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8)): + dependencies: + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) + vite-plugin-restart@0.4.1(vite@5.4.3(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8)): dependencies: micromatch: 4.0.7 @@ -17144,17 +17316,6 @@ snapshots: - typescript vite@5.4.3(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.45 - rollup: 4.21.2 - optionalDependencies: - '@types/node': 20.14.12 - fsevents: 2.3.3 - sass: 1.77.8 - stylus: 0.54.8 - - vite@5.4.6(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8): dependencies: esbuild: 0.21.5 postcss: 8.4.45 @@ -17165,18 +17326,7 @@ snapshots: sass: 1.77.8 stylus: 0.54.8 - vite@5.4.6(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8): - dependencies: - esbuild: 0.21.5 - postcss: 8.4.45 - rollup: 4.22.4 - optionalDependencies: - '@types/node': 22.7.4 - fsevents: 2.3.3 - sass: 1.77.8 - stylus: 0.54.8 - - vite@5.4.8(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8): + vite@5.4.6(@types/node@20.14.12)(sass@1.77.8)(stylus@0.54.8): dependencies: esbuild: 0.21.5 postcss: 8.4.45 @@ -17187,21 +17337,21 @@ snapshots: sass: 1.77.8 stylus: 0.54.8 - vite@5.4.8(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8): + vite@5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8): dependencies: esbuild: 0.21.5 postcss: 8.4.45 rollup: 4.21.2 optionalDependencies: - '@types/node': 22.7.4 + '@types/node': 22.5.0 fsevents: 2.3.3 sass: 1.77.8 stylus: 0.54.8 - vitest-github-actions-reporter@0.11.1(vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)): + vitest-github-actions-reporter@0.11.1(vitest@2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8)): dependencies: '@actions/core': 1.10.1 - vitest: 2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) + vitest: 2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8) vitest@2.0.5(@types/node@20.14.12)(happy-dom@15.7.3)(sass@1.77.8)(stylus@0.54.8): dependencies: @@ -17271,7 +17421,7 @@ snapshots: - supports-color - terser - vitest@2.0.5(@types/node@22.7.4)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8): + vitest@2.0.5(@types/node@22.5.0)(happy-dom@15.7.4)(sass@1.77.8)(stylus@0.54.8): dependencies: '@ampproject/remapping': 2.3.0 '@vitest/expect': 2.0.5 @@ -17289,11 +17439,11 @@ snapshots: tinybench: 2.9.0 tinypool: 1.0.0 tinyrainbow: 1.2.0 - vite: 5.4.8(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8) - vite-node: 2.0.5(@types/node@22.7.4)(sass@1.77.8)(stylus@0.54.8) + vite: 5.4.6(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) + vite-node: 2.0.5(@types/node@22.5.0)(sass@1.77.8)(stylus@0.54.8) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.7.4 + '@types/node': 22.5.0 happy-dom: 15.7.4 transitivePeerDependencies: - less @@ -17341,23 +17491,23 @@ snapshots: optionalDependencies: typescript: 5.5.4 - vuepress-plugin-components@2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): + vuepress-plugin-components@2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): dependencies: '@stackblitz/sdk': 1.11.0 - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) balloon-css: 1.2.0 create-codepen: 2.0.0 qrcode: 1.5.4 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) - vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) transitivePeerDependencies: - '@vue/composition-api' - typescript - vuepress-plugin-md-enhance@2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): + vuepress-plugin-md-enhance@2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): dependencies: '@mdit/plugin-alert': 0.12.0(markdown-it@14.1.0) '@mdit/plugin-align': 0.12.0(markdown-it@14.1.0) @@ -17383,14 +17533,14 @@ snapshots: '@mdit/plugin-tex': 0.12.0(markdown-it@14.1.0) '@mdit/plugin-uml': 0.12.0(markdown-it@14.1.0) '@types/markdown-it': 14.1.2 - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) balloon-css: 1.2.0 js-yaml: 4.1.0 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) - vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) optionalDependencies: katex: 0.16.11 mermaid: 11.0.2 @@ -17399,9 +17549,9 @@ snapshots: - markdown-it - typescript - vuepress-shared@2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): + vuepress-shared@2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) cheerio: 1.0.0-rc.12 dayjs: 1.11.13 @@ -17410,34 +17560,34 @@ snapshots: gray-matter: 4.0.3 semver: 7.6.3 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) transitivePeerDependencies: - '@vue/composition-api' - typescript - vuepress-theme-hope@2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): - dependencies: - '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-active-header-links': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-back-to-top': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-blog': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-catalog': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-comment': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-copy-code': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-copyright': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-git': 2.0.0-rc.38(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-links-check': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-notice': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-nprogress': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-photo-swipe': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-reading-time': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-rtl': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-seo': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-shiki': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-sitemap': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-theme-data': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - '@vuepress/plugin-watermark': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress-theme-hope@2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))): + dependencies: + '@vuepress/helper': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-active-header-links': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-back-to-top': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-blog': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-catalog': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-comment': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-copy-code': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-copyright': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-git': 2.0.0-rc.38(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-links-check': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-notice': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-nprogress': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-photo-swipe': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-reading-time': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-rtl': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-sass-palette': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-seo': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-shiki': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-sitemap': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-theme-data': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + '@vuepress/plugin-watermark': 2.0.0-rc.39(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) '@vueuse/core': 10.11.1(vue@3.4.38(typescript@5.5.4)) balloon-css: 1.2.0 bcrypt-ts: 5.0.2 @@ -17445,10 +17595,10 @@ snapshots: chokidar: 3.6.0 gray-matter: 4.0.3 vue: 3.4.38(typescript@5.5.4) - vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) - vuepress-plugin-components: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - vuepress-plugin-md-enhance: 2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) - vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress: 2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)) + vuepress-plugin-components: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress-plugin-md-enhance: 2.0.0-rc.52(katex@0.16.11)(markdown-it@14.1.0)(mermaid@11.0.2)(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) + vuepress-shared: 2.0.0-rc.52(typescript@5.5.4)(vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4))) transitivePeerDependencies: - '@types/reveal.js' - '@vue/composition-api' @@ -17476,7 +17626,7 @@ snapshots: - typescript - vidstack - vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)): + vuepress@2.0.0-rc.15(@vuepress/bundler-vite@2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0))(typescript@5.5.4)(vue@3.4.38(typescript@5.5.4)): dependencies: '@vuepress/cli': 2.0.0-rc.15(typescript@5.5.4) '@vuepress/client': 2.0.0-rc.15(typescript@5.5.4) @@ -17486,7 +17636,7 @@ snapshots: '@vuepress/utils': 2.0.0-rc.15 vue: 3.4.38(typescript@5.5.4) optionalDependencies: - '@vuepress/bundler-vite': 2.0.0-rc.15(@types/node@22.7.4)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0) + '@vuepress/bundler-vite': 2.0.0-rc.15(@types/node@22.5.0)(jiti@1.21.6)(sass@1.77.8)(stylus@0.54.8)(typescript@5.5.4)(yaml@2.5.0) transitivePeerDependencies: - supports-color - typescript @@ -17499,6 +17649,8 @@ snapshots: dependencies: defaults: 1.0.4 + web-streams-polyfill@3.3.3: {} + webidl-conversions@3.0.1: {} webidl-conversions@7.0.0: {} diff --git a/vue-press/src/.vuepress/sidebar.ts b/vue-press/src/.vuepress/sidebar.ts index 86e9b5de30..727a9c75c4 100644 --- a/vue-press/src/.vuepress/sidebar.ts +++ b/vue-press/src/.vuepress/sidebar.ts @@ -36,13 +36,20 @@ export default sidebar({ ], '/cli/': [ { - text: 'App', - link: 'docs/app.md', - prefix: 'docs/', + text: 'CLI', + link: 'README.md', children: [ { - text: 'configuration', - link: 'app-config.md', + text: 'Commands', + link: 'docs/commands.md', + }, + { + text: 'App Configuration', + link: 'docs/configuration.md', + }, + { + text: 'Api Authentication', + link: 'docs/api-authentication.md', }, ], }, diff --git a/vue-press/src/cli/README.md b/vue-press/src/cli/README.md index 2a8e786831..df3b991f03 100644 --- a/vue-press/src/cli/README.md +++ b/vue-press/src/cli/README.md @@ -1,6 +1,6 @@ --- title: Framework CLI -category: Guide +category: CLI tag: - cli --- diff --git a/vue-press/src/cli/docs/api-authentication.md b/vue-press/src/cli/docs/api-authentication.md new file mode 100644 index 0000000000..2ac40521e5 --- /dev/null +++ b/vue-press/src/cli/docs/api-authentication.md @@ -0,0 +1,10 @@ +--- +title: Framework CLI - authentication +category: CLI +tag: + - cli + - api + - authentication +--- + + \ No newline at end of file diff --git a/vue-press/src/cli/docs/app-config.md b/vue-press/src/cli/docs/app-config.md deleted file mode 100644 index 823666860a..0000000000 --- a/vue-press/src/cli/docs/app-config.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Framework CLI -category: Guide -tag: - - cli - - app - - configuration ---- - - \ No newline at end of file diff --git a/vue-press/src/cli/docs/app.md b/vue-press/src/cli/docs/app.md deleted file mode 100644 index 921a89f16b..0000000000 --- a/vue-press/src/cli/docs/app.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: Framework CLI -category: Guide -tag: - - cli - - app ---- - - \ No newline at end of file diff --git a/vue-press/src/cli/docs/commands.md b/vue-press/src/cli/docs/commands.md new file mode 100644 index 0000000000..4ef4f0dc7b --- /dev/null +++ b/vue-press/src/cli/docs/commands.md @@ -0,0 +1,9 @@ +--- +title: Framework CLI +category: CLI +tag: + - cli + - commands +--- + + \ No newline at end of file diff --git a/vue-press/src/cli/docs/configuration.md b/vue-press/src/cli/docs/configuration.md new file mode 100644 index 0000000000..e9003aa570 --- /dev/null +++ b/vue-press/src/cli/docs/configuration.md @@ -0,0 +1,10 @@ +--- +title: Framework CLI - App Configuration +category: CLI +tag: + - cli + - app + - config +--- + + \ No newline at end of file diff --git a/vue-press/src/guide/app/cli.md b/vue-press/src/guide/app/cli.md index cf9a33b9a2..6887fcadd4 100644 --- a/vue-press/src/guide/app/cli.md +++ b/vue-press/src/guide/app/cli.md @@ -10,30 +10,32 @@ tag: ![CLI](./cli.png) +## Installation + ```sh npm i -D '@equinor/fusion-framework-cli' ``` ## Config (optional) - - ```ts // app.config.ts -import { mergeAppConfigs, defineAppConfig } from '@equinor/fusion-framework-cli'; -export default defineAppConfig((_env, { base }) => - mergeAppConfigs(base, { +import { defineAppConfig } from '@equinor/fusion-framework-cli'; +export default defineAppConfig() => ( + { environment: { - endpoints: { - api: { - baseUri: 'https://foo.bar', - defaultScopes: ['default'] - } - } + scope: 'default', + }, + endpoints: { + api: { + url: 'https://api.example.com', + }, }, - }), + } ); +``` +```ts // src/config.ts import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app'; export const configure: AppModuleInitiator = (configurator, { env }) => { @@ -47,17 +49,13 @@ export const configure: AppModuleInitiator = (configurator, { env }) => { By default the CLI will create a manifest on best effort from `package.json` ```ts -// app.manifest.config.ts -import { defineAppManifest, mergeManifests } from '@equinor/fusion-framework-cli'; +// app.manifest.config.ts should be of type AppManifestExport +import { defineAppManifest } from '@equinor/fusion-framework-cli'; -export default defineAppManifest((env, { base }) => { - return mergeManifests( - base, - { - /** override name from package.json */ - "appKey": "my-key", - } - ) +export default defineAppManifest(() => ({ + build: { + entryPoint: 'index.js', + }, }); ``` @@ -89,4 +87,4 @@ fusion-framework-cli app pack > its important to set your package type to module to generate a proper app-bundle for use in the Fusion portal, add `"type": "module"` to your package.json. -[read more about CLI](../../cli/docs/app.md) +[read more about CLI](../../cli/index.md) diff --git a/vue-press/src/guide/app/getting-started.md b/vue-press/src/guide/app/getting-started.md index c06b13e1d0..46c850bb0a 100644 --- a/vue-press/src/guide/app/getting-started.md +++ b/vue-press/src/guide/app/getting-started.md @@ -50,7 +50,7 @@ npm i -D '@equinor/fusion-framework-react-app' ], } } -``` +``` ::: ### EsLint @@ -59,7 +59,7 @@ npm i -D '@equinor/fusion-framework-react-app' @tab .eslintrc ```json { - "extends": "@equinor/eslint-config-fusion/react", + "extends": "@equinor/eslint-config-fusion-react", } ``` @@ -90,30 +90,42 @@ When the application renders, the portal will load configuration from `app servi ::: code-tabs @tab src/config.ts + ```ts -export const configure = (configurator, { env }) => { +import type { AppModuleInitiator } from '@equinor/fusion-framework-react-app'; - const { config: { environment: { endpoints } } } = env; +import { enableContext } from '@equinor/fusion-framework-module-context'; - for(const [key, endpoint] in Object.entries(endpoints)) { - const { baseUri, defaultScopes } = endpoint; - configurator.configureHttpClient({ key, { baseUri, defaultScopes }); - } +export const configure: AppModuleInitiator = (configurator) => { + // enable context and set contextType + enableContext(configurator, async (builder) => { + builder.setContextType(['projectMaster']); + }); + + // configure framework loglevel + configurator.logger.level = 0; }; + +export default configure; ``` -@tab app.config.js -```js -export default () => ({ - "environment": { - "endpoints": { - "api": { - "baseUri": "https://foo.barz", - "defaultScopes": ["foobar/.default"] - } - } - } -}); +@tab app.config.ts + +```ts +import { mergeAppConfigs, defineAppConfig } from '@equinor/fusion-framework-cli'; +export default defineAppConfig((_nev, { base }) => + mergeAppConfigs(base, { + environment: { + scope: 'foobar', + }, + endpoints: { + api: { + url: 'https://foo.bars', + }, + }, + }), +); + ``` :::