diff --git a/.env b/.env deleted file mode 100644 index 08e68bf3..00000000 --- a/.env +++ /dev/null @@ -1,22 +0,0 @@ -NODE_ENV='production' -ACCESS_TOKEN_COOKIE_NAME='' -BASE_URL='' -CREDENTIALS_BASE_URL='' -CSRF_TOKEN_API_PATH='' -ECOMMERCE_BASE_URL='' -LANGUAGE_PREFERENCE_COOKIE_NAME='' -LMS_BASE_URL='' -LOGIN_URL='' -LOGOUT_URL='' -LOGO_URL='' -LOGO_TRADEMARK_URL='' -LOGO_WHITE_URL='' -FAVICON_URL='' -MARKETING_SITE_BASE_URL='' -ORDER_HISTORY_URL='' -REFRESH_ACCESS_TOKEN_ENDPOINT='' -SEGMENT_KEY='' -SITE_NAME='' -USER_INFO_COOKIE_NAME='' -APP_ID='' -MFE_CONFIG_API_URL='' diff --git a/.env.development b/.env.development deleted file mode 100644 index 21974f26..00000000 --- a/.env.development +++ /dev/null @@ -1,30 +0,0 @@ -EXAMPLE_VAR=Example Value -ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload -ACCOUNT_PROFILE_URL=http://localhost:1995 -ACCOUNT_SETTINGS_URL=http://localhost:1997 -BASE_URL=http://localhost:8080 -CREDENTIALS_BASE_URL=http://localhost:18150 -CSRF_TOKEN_API_PATH=/csrf/api/v1/token -DISCOVERY_API_BASE_URL=http://localhost:18381 -PUBLISHER_BASE_URL=http://localhost:18400 -ECOMMERCE_BASE_URL=http://localhost:18130 -LANGUAGE_PREFERENCE_COOKIE_NAME=openedx-language-preference -LEARNING_BASE_URL=http://localhost:2000 -LMS_BASE_URL=http://localhost:18000 -LOGIN_URL=http://localhost:18000/login -LOGOUT_URL=http://localhost:18000/logout -STUDIO_BASE_URL=http://localhost:18010 -MARKETING_SITE_BASE_URL=http://localhost:18000 -ORDER_HISTORY_URL=http://localhost:1996/orders -REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh -SEGMENT_KEY='' -SITE_NAME=localhost -USER_INFO_COOKIE_NAME=edx-user-info -LOGO_URL=https://edx-cdn.org/v3/default/logo.svg -LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg -LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg -FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico -IGNORED_ERROR_REGEX= -MFE_CONFIG_API_URL= -APP_ID= -SUPPORT_URL=https://support.edx.org \ No newline at end of file diff --git a/.env.test b/.env.test deleted file mode 100644 index f25231a3..00000000 --- a/.env.test +++ /dev/null @@ -1,30 +0,0 @@ -EXAMPLE_VAR=Example Value -ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload -ACCOUNT_PROFILE_URL=http://localhost:1995 -ACCOUNT_SETTINGS_URL=http://localhost:1997 -BASE_URL=http://localhost:8080 -CREDENTIALS_BASE_URL=http://localhost:18150 -CSRF_TOKEN_API_PATH=/csrf/api/v1/token -DISCOVERY_API_BASE_URL=http://localhost:18381 -PUBLISHER_BASE_URL=http://localhost:18400 -ECOMMERCE_BASE_URL=http://localhost:18130 -LANGUAGE_PREFERENCE_COOKIE_NAME=openedx-language-preference -LEARNING_BASE_URL=http://localhost:2000 -LMS_BASE_URL=http://localhost:18000 -LOGIN_URL=http://localhost:18000/login -LOGOUT_URL=http://localhost:18000/logout -STUDIO_BASE_URL=http://localhost:18010 -MARKETING_SITE_BASE_URL=http://localhost:18000 -ORDER_HISTORY_URL=http://localhost:1996/orders -REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh -SEGMENT_KEY='' -SITE_NAME=localhost -USER_INFO_COOKIE_NAME=edx-user-info -LOGO_URL=https://edx-cdn.org/v3/default/logo.svg -LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg -LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg -FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico -IGNORED_ERROR_REGEX= -MFE_CONFIG_API_URL= -APP_ID= -SUPPORT_URL=https://support.edx.org diff --git a/.eslintrc.js b/.eslintrc.js index eb94268c..67904ec5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,9 @@ const path = require('path'); +const { merge } = require('webpack-merge'); -const { createConfig } = require('./config'); +const config = require('./tools/eslint/.eslintrc.js'); -module.exports = createConfig('eslint', { +module.exports = merge(config, { ignorePatterns: [ 'test-app', 'docs', @@ -11,6 +12,8 @@ module.exports = createConfig('eslint', { 'coverage', 'example', 'example-plugin-app', + 'tools', + 'config', ], parserOptions: { project: path.resolve(__dirname, './tsconfig.json'), diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b972a91..d6dc3a46 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,11 @@ jobs: - name: Install dependencies run: | npm ci + npm run build + npm pack + mv openedx-frontend-base* openedx-frontend-base.tgz cd test-app + npm i ../openedx-frontend-base.tgz npm ci - name: Lint run: npm run lint diff --git a/.gitignore b/.gitignore index 4a85a606..44c2a7b3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ .vscode coverage dist +config +scss node_modules npm-debug.log docs/api diff --git a/Makefile b/Makefile index d662cf2c..b55924a2 100644 --- a/Makefile +++ b/Makefile @@ -11,15 +11,18 @@ doc_command = ./node_modules/.bin/documentation build src -g -c ./docs/documenta cat_docs_command = cat ./docs/_API-header.md ./docs/_API-body.md > ./docs/API.md build: - rm -rf ./dist - tsc - cp frontend-base.d.ts dist/frontend-base.d.ts - mkdir -p ./scss/header/studio-header - - cp shell/index.scss dist/shell/index.scss - cp shell/header/index.scss dist/shell/header/index.scss - cp shell/header/Menu/menu.scss dist/shell/header/Menu/menu.scss - cp shell/header/studio-header/StudioHeader.scss dist/shell/header/studio-header/StudioHeader.scss + rm -rf ./config ./dist + tsc --project tsconfig.build.json + mkdir -p ./config + cp tools/typescript/tsconfig.json config/tsconfig.json + tsc --project ./tools/tsconfig.json + cp -prf ./tools/dist ./dist + mv ./dist/dist ./dist/tools + cp -prf ./tools/dist/config-helpers ./config/config-helpers + cp -prf ./tools/dist/eslint ./config/eslint + cp -prf ./tools/dist/jest ./config/jest + cp -prf ./tools/dist/webpack ./config/webpack + cp -prf ./tools/dist/index.js ./config/index.js docs-build: ${doc_command} @@ -33,7 +36,6 @@ docs-watch: docs-lint: ./node_modules/.bin/documentation lint src - .PHONY: requirements requirements: ## install ci requirements npm ci diff --git a/README.md b/README.md index d12aca96..cef1e9dd 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ It will enable Open edX frontends to be loaded as "module plugins" via Webpack m ## Further reading +- [Frontend Glossary](./docs/frontend-glossary.md) - [OEP-65: Frontend composability](https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0065-arch-frontend-composability.html) - [ADR 0001: Create a unified platform library](https://github.com/openedx/open-edx-proposals/pull/598) - [Discourse discussion on frontend projects](https://discuss.openedx.org/t/oep-65-adjacent-a-frontend-architecture-vision/13223) @@ -36,9 +37,80 @@ Feel free to reach out to David Joy ([Github](https://github.com/davidjoy), [Sla ## Development -This library is not yet published to npm. +This library is under development and has not yet been published to npm. -In the meantime, it can be used as a replacement for `openedx/frontend-build` in an Open edX micro-frontend in a few steps. +Its functionality can be tested with a few other companion repositories that have been built to work with it: + +- https://github.com/davidjoy/frontend-app-base-test +- https://github.com/davidjoy/frontend-project-test +- https://github.com/davidjoy/frontend-project-module-test + +To start the whole thing together: + +- Check out https://github.com/davidjoy/frontend-base and the above three repositories as siblings. +- Run `npm install` on all of them. +- Run `npm run temp:pack-it` in frontend-base. +- Run `npm run pack frontend-project-test` in frontend-app-base-test. +- Run `npm run pack frontend-project-module-test` in frontend-app-base-test. +- You'll need 3 terminal windows. + - Run `npm run dev` in `frontend-project-test` + - Run `npm run dev:module` in `frontend-app-base-test` + - Run `npm run dev:module` in `frontend-project-module-test +- Visit `http://localhost:8080` in your browser to see the unified site. + +What this site is showing you: + +- The shell (header, footer, app initialization) is being loaded from `frontend-base` through `frontend-project-test`. The `site.config.dev.tsx` file in `frontend-project-test` has configured the site. +- `ModuleOne` from `frontend-app-base-test` is being loaded _through_ the `site.config.dev.tsx` file in `frontend-project-test` as an imported dependency. +- `ModuleTwo` is being loaded at runtime from the module federation dev server in `frontend-app-base-test`. +- `ModuleThree` is being loaded at runtime from the module federation dev server in `frontend-project-module-test`. + +Read below for more details about the companion repositories. + +### frontend-app-base-test + +https://github.com/davidjoy/frontend-app-base-test + +This is an "MFE" repository with three modules in it which can be loaded into a shell. It can be tested in a few different ways. + +- `npm run dev:module` - This will run a dev server and build the app as modules for module federation. +- `npm run dev` - This will run the a dev server and build the app as part of a shell with three imported modules. +- `npm run pack ` - this will package the library into an npm compatible `.tgz` file for use with the project repositories below. + +### frontend-project-test + +This is a "project" repository, which is a new thing. A project is where you put your customizations to the frontend. + +The `frontend-project-test` project has been configured in its `site.config.dev.tsx` file to load the three modules from `frontend-app-base-test` in three different ways: + +- Module One is loaded as an imported app, using frontend-app-base-test as a dependency of the project installed via `npm run pack` above. +- Module Two is loaded via module federation from the `npm run dev:module` dev server in `frontend-app-base-test` +- Module Three is loaded via module federation from the `npm run dev:module` dev server in `frontend-project-module-test`. + +It can be tested with: + +- `npm run dev` - This will start up a dev server with the new shell application and load the three modules into it via the methods described above. + +### frontend-project-module-test + +This is also a "project" repository, demonstrating that you can use module federation with a released library version of `frontend-app-base-test`, rather than by cloning the app and running a dev server in it. This is more appropriate for customizations of the app because you can check all your customizations in to the project, rather than needing to copy/paste them into the frontend-app-* repository at build time, which is awkward. + +It can be tested with: + +- `npm run dev:module` - This starts up a dev server that serves the modules from `frontend-app-base-test` for module federation, the same as running `npm run dev:module` in the `frontend-app-base-test` repository itself. + +It's worth noting that this project has a new type of file: `build.dev.config.js` which is necessary to configure webpack to understand what modules it should be packaging for module federation. We can't use `site.config.dev.tsx` for this purpose, since that's runtime code. + + +## Migrating an MFE to `frontend-base` (Work in progress) + +| :rotating_light: Pre-alpha | +|:------------------------------------------------------------------------------------------| +| This library is not yet published to NPM. It is not ready for production use, and you should only migrate an MFE on a branch as a test. | + +To use `frontend-base`, you'll need to use `npm pack` and install it into your MFE from the resulting `.tgz` file. + +Following these steps turns your MFE into a library that can be built using frontend-base and its shell application. It involves deleting a lot of unneeded dependencies and code. ### 1. Clone this repository @@ -46,7 +118,7 @@ In the meantime, it can be used as a replacement for `openedx/frontend-build` in ### 2. `npm install` and `npm run build` in frontend-base -You'll need to install dependencies and then build this repo at least once. If you want the build process to watch for changes, you can do `npm run build:watch` instead. +You'll need to install dependencies and then build this repo at least once. ### 3. Change dependencies in package.json in MFE @@ -54,33 +126,29 @@ We need to: - Uninstall `@edx/frontend-platform` - Uninstall `@openedx/frontend-build` + - Add frontend-base to dependencies ``` npm uninstall @edx/frontend-platform @openedx/frontend-build ``` -And then manually add `frontend-base` to package.json's dependencies: +And then use `npm pack` and `npm i` to add `frontend-base` to package.json's dependencies: -``` -"dependencies": { -+ "@openedx/frontend-base": "file:../frontend-base", -}, -``` +- In `frontend-base` run `npm pack`. A file named `openedx-frontend-base-1.0.0.tgz` will be created. +- In your MFe, run `npm i --save-peer ../frontend-base/openedx-frontend-base-1.0.0.tgz` -After doing these two steps, your package.json should have changed in this way: +Your package.json should now have a line like this: ``` "dependencies": { -+ "@openedx/frontend-base": "file:../frontend-base", -- "@edx/frontend-platform": "@edx/frontend-platform@" ++ "@openedx/frontend-base": "file:../frontend-base/openedx-frontend-base-1.0.0.tgz", }, -"devDependencies": { -- "@openedx/frontend-build": "@openedx/frontend-build@" -} ``` -This will let your MFE use the checked out version of `frontend-base`. +If `frontend-base` changes, you'll need to repeat these steps. + +After doing these two steps, your package.json should have changed in this way: ### 4. `npm install`in MFE @@ -92,34 +160,35 @@ rm package-lock.json npm install ``` -### 5. Add frontend-base to module.config.js in MFE - -To use a local version of frontend-base properly, you need to add it to module.config.js. Add the following line to your module.config.js file in your MFE: +### 5. Edit package.json `scripts` -```diff -module.exports = { - localModules: [ -+ { moduleName: '@openedx/frontend-base', dir: '../frontend-base', dist: 'dist' }, - ], -}; +With the exception of any custom scripts, replace the `scripts` section of your MFE's package.json file with the following: +``` + "scripts": { + "build:module": "PORT=YOUR_PORT openedx build:module", + "dev": "PORT=YOUR_PORT openedx dev", + "dev:module": "PORT=YOUR_PORT openedx dev:module", + "i18n_extract": "openedx formatjs extract", + "lint": "openedx lint .", + "lint:fix": "openedx lint --fix .", + "pack": "openedx pack", + "release": "openedx release", + "serve": "PORT=YOUR_PORT openedx serve", + "snapshot": "openedx test --updateSnapshot", + "test": "openedx test --coverage --passWithNoTests" + }, ``` -### 6. Migrate your MFE - -Follow the steps below to migrate an MFE to use frontend-base. - -## Migrating to frontend-base (no shell) - -### 1. Edit package.json `scripts` +- Replace `YOUR_PORT` with the desired port, of course. +- Note that `fedx-scripts` no longer exists, and has been replaced with `openedx`. -Replace all instances of `fedx-scripts` with `openedx` in your package.json file. > [!TIP] > **Why change `fedx-scripts` to `openedx`?** > A few reasons. One, the Open edX project shouldn't be using the name of an internal community of practice at edX for its frontend tooling. Two, some dependencies of your MFE invariably still use frontend-build for their own build needs. This means that they already installed `fedx-scripts` into your `node_modules/.bin` folder. Only one version can be in there, so we need a new name. Seemed like a great time for a naming refresh. | -### 2. Add a Type Declaration file (app.d.ts) +### 6. Add a Type Declaration file (app.d.ts) Create an `app.d.ts` file in the root of your MFE with the following contents: @@ -134,39 +203,53 @@ declare module "*.svg" { This will ensure that you can import SVG files, and have type declarations for the frontend-base library. -### 3. Add a tsconfig.json file +### 7. Add a tsconfig JSON files -Create a tsconfig.json file and add the following contents to it: +Create a `tsconfig.json` file and add the following contents to it: ``` { "extends": "@openedx/frontend-base/config/tsconfig.json", "compilerOptions": { "rootDir": ".", - "outDir": "dist" + "outDir": "dist", }, "include": [ - ".eslintrc.js", - "jest.config.js", - "env.config.js", "src/**/*", "app.d.ts", + "babel.config.js", + ".eslintrc.js", + "jest.config.js", + "test.site.config.tsx", + "site.config.tsx", + "site.config.*.tsx", ] } ``` -This assumes you have a `src` folder and your build goes in `dist`, which is the best practice. - -### 4. Edit `jest.config.js` - -Replace the import from 'frontend-build' with 'frontend-base'. +Create a `tsconfig.build.json` file and add the following contents to it: -```diff -- const { createConfig } = require('@openedx/frontend-build'); -+ const { createConfig } = require('@openedx/frontend-base/config'); ``` +{ + "extends": "@openedx/frontend-base/config/tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "noEmit": false, + }, + "include": [ + "src/**/*", + ], + "exclude": [ + "src/__mocks__", + "src/setupTest.js", + ] +} +``` + +This assumes you have a `src` folder and your build goes in `dist`, which is the best practice. -### 5. Edit `.eslintrc.js` +### 8. Edit `jest.config.js` Replace the import from 'frontend-build' with 'frontend-base'. @@ -175,16 +258,6 @@ Replace the import from 'frontend-build' with 'frontend-base'. + const { createConfig } = require('@openedx/frontend-base/config'); ``` -### 6. Search for any other usages of `frontend-build` - -Find any other imports/usages of `frontend-build` in your repository and replace them with `frontend-base` so they don't break. - -### 7. i18n Descriptions - -Description fields are now required on all i18n messages in the repository. This is because of a change to the ESLint config. - -### 8. Jest Mocks - Jest test suites that test React components that import SVG and files must add mocks for those filetypes. This can be accomplished by adding the following module name mappers to jest.config.js: ``` @@ -210,26 +283,82 @@ module.exports = 'FileMock'; You can change the values of "SvgURL", and "FileMock" if you want to reduce changes necessary to your snapshot tests; the old values from frontend-build assume svg is only being used for icons, so the values referenced an "icon" which felt unnecessarily narrow. -This is necessary because we cannot write a tsconfig.json in MFEs that includes transpilation of the "config/jest" folder in frontend-base, it can't meaningfully find those files and transpile them, and we wouldn't want all MFEs to have to include such idiosyncratic configuration anyway. The SVG mock, however, requires ESModules syntax to export its default and ReactComponent exports at the same time. This means without moving the mocks into the MFE code, the SVG one breaks transpilation and doesn't understand the `export` syntax used. By moving them into the MFE, they can be easily transpiled along with all the other code when jest tries to run. +This is necessary because we cannot write a tsconfig.json in MFEs that includes transpilation of the "tools/jest" folder in frontend-base, it can't meaningfully find those files and transpile them, and we wouldn't want all MFEs to have to include such idiosyncratic configuration anyway. The SVG mock, however, requires ESModules syntax to export its default and ReactComponent exports at the same time. This means without moving the mocks into the MFE code, the SVG one breaks transpilation and doesn't understand the `export` syntax used. By moving them into the MFE, they can be easily transpiled along with all the other code when jest tries to run. -### 9. SVGR "ReactComponent" imports have been removed. +#### Resulting `jest.config.js` file -We have removed the `@svgr/webpack` loader because it was incompatible with more modern tooling (it requires Babel). As a result, the ability to import SVG files into JS as the `ReactComponent` export no longer works. We know of a total of 5 places where this is happening today in Open edX MFEs - frontend-app-learning and frontend-app-profile use it. Please replace that export with the default URL export and set the URL as the source of an `` tag, rather than using `ReactComponent`. You can see an example of normal SVG imports in `test-app/src/App.jsx`. +An uncustomized jest.config.js looks like: -### 10. Import `createConfig` and `getBaseConfig` from `@openedx/frontend-base/config` +``` +const { createConfig } = require('@openedx/frontend-base/config'); -In frontend-build, `createConfig` and `getBaseConfig` could be imported from the root package (`@openedx/frontend-build`). They have been moved to a sub-directory to make room for runtime exports from the root package (`@openedx/frontend-base`). +module.exports = createConfig('jest', { + // setupFilesAfterEnv is used after the jest environment has been loaded. In general this is what you want. + // If you want to add config BEFORE jest loads, use setupFiles instead. + setupFilesAfterEnv: [ + '/src/setupTest.js', + ], + coveragePathIgnorePatterns: [ + 'src/setupTest.js', + 'src/i18n', + 'src/__mocks__', + ], + moduleNameMapper: { + '\\.svg$': '/src/__mocks__/svg.js', + '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/src/__mocks__/file.js', + }, +}); +``` + +### 9. Edit `.eslintrc.js` + +Replace the import from 'frontend-build' with 'frontend-base'. ```diff - const { createConfig } = require('@openedx/frontend-build'); + const { createConfig } = require('@openedx/frontend-base/config'); ``` +You will also need to set the `project` in `parserOptions`. An uncustomized `.eslintrc.js` file looks like: + +``` +const path = require('path'); + +const { createConfig } = require('@openedx/frontend-base/config'); + +module.exports = createConfig('eslint', { + parserOptions: { + project: path.resolve(__dirname, './tsconfig.json'), + }, +}); +``` + +### 10. Search for any other usages of `frontend-build` + +Find any other imports/usages of `frontend-build` in your repository and replace them with `frontend-base` so they don't break. + +### 11. i18n Descriptions + +Description fields are now required on all i18n messages in the repository. This is because of a change to the ESLint config. + +### 12. SVGR "ReactComponent" imports have been removed. + +We have removed the `@svgr/webpack` loader because it was incompatible with more modern tooling (it requires Babel). As a result, the ability to import SVG files into JS as the `ReactComponent` export no longer works. We know of a total of 5 places where this is happening today in Open edX MFEs - frontend-app-learning and frontend-app-profile use it. Please replace that export with the default URL export and set the URL as the source of an `` tag, rather than using `ReactComponent`. You can see an example of normal SVG imports in `test-app/src/App.jsx`. + +### 13. Import `createConfig` and `getBaseConfig` from `@openedx/frontend-base/config` + +In frontend-build, `createConfig` and `getBaseConfig` could be imported from the root package (`@openedx/frontend-build`). They have been moved to a sub-directory to make room for runtime exports from the root package (`@openedx/frontend-base`). + +```diff +- const { createConfig, getBaseConfig } = require('@openedx/frontend-build'); ++ const { createConfig, getBaseConfig } = require('@openedx/frontend-base/config'); +``` + You may have handled this in steps 4 and 5 above (jest.config.js and .eslintrc.js) -### 11. Replace all imports from `@edx/frontend-platform` with `@openedx/frontend-base` +### 14. Replace all imports from `@edx/frontend-platform` with `@openedx/frontend-base` -frontend-base includes all exports from frontend-platform. Rather than export them from sub-directories, it exports them all from the root package folder. As an example: +`frontend-base` includes all exports from `frontend-platform`. Rather than export them from sub-directories, it exports them all from the root package folder. As an example: ```diff - import { getConfig } from '@edx/frontend-platform/config'; @@ -244,10 +373,10 @@ frontend-base includes all exports from frontend-platform. Rather than export t Note that the `configure` functions for auth, logging, and analytics are now exported with the names: -- configureAuth -- configureLogging -- configureAnalytics -- configureI18n +- `configureAuth` +- `configureLogging` +- `configureAnalytics` +- `configureI18n` Remember to make the following substitution for these functions: @@ -256,56 +385,50 @@ Remember to make the following substitution for these functions: + import { configureLogging } from '@openedx/frontend-base'; ``` -## Migrating to the frontend-base shell (:rotating_light: Work In Progress) +### 15. Replace the .env.test file with configuration in test.site.config.tsx file -This is an interim migration that helps prepare an MFE to be released as a set of modules suitable for module federation or being included as a plugin into the shell application. +We're moving away from .env files because they're not expressive enough (only string types!) to configure an Open edX frontend. Instead, the test suite has been configured to expect an test.site.config.tsx file. If you're initializing an application in your tests, frontend-base will pick up this configuration and make it available to getConfig(), etc. If you need to manually access the variables, you an import `site.config` in your test files: -This migration may require some refactoring of the top-level files of the application; it is less straight-forward than simply replacing frontend-build and frontend-platform with frontend-base. +```diff ++ import config from 'site.config'; +``` -Prior to attempting this, you _must_ complete the steps above to migrate your MFE to use frontend-base instead of frontend-build and frontend-platform. +The Jest configuration has been set up to find `site.config` at an `test.site.config.tsx` file. -In spirit, in this migration we remove the initialization and header/footer code from the MFE and instead rely on the shell to manage our application. We turn the MFE into a set of exported modules, and to maintain backwards compatibility, we create a small 'project' in the repository that helps us build the MFE as an independent application. +### 16. Remove initialization -### 1. Remove initialization +In your index.(jsx|tsx) file, you need to remove the subscribe and initialization code. If you have customizations here, they will need to migrate to your `site.config` file instead and take advantage of the shell's provided customization mechanisms. **This functionality is still a work in progress.** -In your index.(jsx|tsx) file, you need to remove the subscribe and initialization code. If you have customizations here, they will need to migrate to your env.config.tsx file instead and take advantage of the shell's provided customization mechanisms. +### 17. Migrate header/footer dependencies -### 2. Migrate header/footer dependencies +If your application uses a custom header or footer, you can use the shell's header and footer plugin slots to provide your custom header/footer components. This is done through the `site.config` file. **This functionality is still a work in progress.** -If your application uses a custom header or footer, you can use the shell's header and footer plugin slots to provide your custom header/footer components. This is done through the env.config.tsx file. +### 18. Export the modules of your app in your index.ts file. -### 3. Export the modules of your app as a component. +This may require a little interpretation. In spirit, the modules of your app are the 'pages' of an Open edX Frontend site that it provides. This likely corresponds to the top-level react-router routes in your app. In frontend-app-profile, for instance, this is the `ProfilePage` component, amongst a few others. Some MFEs have put their router and pages directly into the `index.jsx` file inside the initialization callback - this code will need to be moved to a single component that can be exported. -This may require a little interpretation. In spirit, the modules of your app are the 'pages' of an Open edX Frontend site that it provides. This likely corresponds to the top-level react-router routes in your app. At the time of this writing, we don't have module federation yet, so to use the shell, you export all of your application code in a single component. In frontend-app-profile, for instance, this is the ProfilePage component. Some MFEs have put their router and pages directly into the index.jsx file inside the initialization callback - this code will need to be moved to a single component that can be exported. +These modules should be unopinionated about the path prefix where they are mounted. The exact way we handle routing is still being figured out. In the short term, the react-router data APIs are not suppored until we can figure out how to implement lazy route discovery (a.k.a., "Fog of War") Using `` with `` components inside it works today. **This functionality is still a work in progress, and is one of the big things we need to figure out.** -### 4. Create a project.scss file +### 19. Create a project.scss file -Create a new project.scss file at the top of your application. It's responsible for: +Create a new `project.scss` file at the top of your application. It's responsible for: 1. Importing the shell's stylesheet, which includes Paragon's core stylesheet. 2. Importing your brand stylesheet. 3. Importing the stylesheets from your application. -You must then import this new stylesheet into your env.config.tsx file: +You must then import this new stylesheet into your `site.config` file: ```diff + import './project.scss'; -const config = { +const config: SiteConfig = { // config document } export default config; ``` -### 5. Add new build scripts to package.json - -After the previous steps, the legacy `build` and `start` scripts in package.json will no longer work properly. They need to be replaced with versions from the `openedx` CLI that: - -- Build the MFE for production using the shell application. -- Build the MFE for dev using the shell application. -- Build the MFE for release as a library. - ## Merging repositories Followed this process: https://stackoverflow.com/questions/13040958/merge-two-git-repositories-without-breaking-file-history @@ -319,3 +442,23 @@ git merge other-repo-remote/master --allow-unrelated-histories Then work through the conflicts and use a merge commit to add the history into the frontend-base. Then move the files out of the way (move src to some other sub-dir, mostly) to make room for the next repo. + +# Other notable changes + +- Cease using `AUTHN_MINIMAL_HEADER`, replace it with an actual minimal header. +- No more using `process.env` in runtime code. +- `SUPPORT_URL` is now optional and the support link in the header is hidden if it's not present. +- Removed dotenv. Use site.config.tsx. +- Removed Purge CSS. We do not believe that Purge CSS works properly with Paragon in general, and it is also fundamentally incompatible with module federation as an architecture. +- Removed `ensureConfig` function. This sort of type safety should happen with TypeScript types in the site config file. +- Removed `ensureDefinedConfig` function. Similar to ensureConfig, this sort of type safety should be handled by TypeScript. +- A number of site config variables now have sensible defaults: + - ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload', + - CSRF_TOKEN_API_PATH: '/csrf/api/v1/token', + - LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference', + - USER_INFO_COOKIE_NAME: 'edx-user-info', + - PUBLIC_PATH: '/', + - ENVIRONMENT: 'production', +- the `basename` and `history` exports have been replaced by function getters: `getBasename` and `getHistory`. This is because it may not be possible to determine the values of the original constants at code initialization time, since our config may arrive asynchronously. This ensures that anyone trying to get these values gets a current value. +- When using MockAuthService, set the authenticated user by calling setAuthenticatedUser after instantiating the service. It's not okay for us to add arbitrary config values to the site config. +- `REFRESH_ACCESS_TOKEN_ENDPOINT` has been replaced with `REFRESH_ACCESS_TOKEN_API_PATH`. It is now a path that defaults to '/login_refresh'. The Auth service assumes it is an endpoint on the LMS, and joins the path with `LMS_BASE_URL`. This change creates more parity with other paths such as `CSRF_TOKEN_API_PATH`. diff --git a/babel.config.js b/babel.config.js index fc59fc9c..96cfc8b6 100644 --- a/babel.config.js +++ b/babel.config.js @@ -1,3 +1,3 @@ -const config = require('./config/babel.config'); +const config = require('./tools/babel/babel.base.config'); module.exports = config; diff --git a/bin/openedx.js b/bin/openedx.js deleted file mode 100755 index e9f3e7ae..00000000 --- a/bin/openedx.js +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env node - -const chalk = require('chalk'); - -const presets = require('../cli/presets'); - -/** - * TLDR: - * - Find the command to be run in process.argv - * - Remove 'openedx' in process.argv - * - Add a --config option to process.argv if one is missing - * - Execute the command's bin script by pulling it directly in with require() - * - * This file forwards cli commands by manipulating process.argv values and then - * directly requiring bin scripts from the specified packages (as opposed to - * attempting to run them from the aliases npm copies to the .bin folder upon - * install). This seems like a relatively safe thing to do since these file - * names are identical to their cli name and this method of requiring/executing - * them should behave the same as if run from the command line as usual. - */ - -function optionExists(keys) { - return process.argv.some((arg) => { - // eslint-disable-next-line no-plusplus - for (let i = 0; i < keys.length; i++) { - if (arg.startsWith(keys[i])) { - return true; - } - } - return false; - }); -} - -// Ensures that a config option already exists and if it does not adds a default -function ensureConfigOption(preset, keys = ['--config', '-c']) { - if (!optionExists(keys)) { - console.log(`Running with resolved config:\n${preset.resolvedFilepath}\n`); - process.argv.push(keys[0]); - process.argv.push(preset.resolvedFilepath); - } -} - -// commandName is the third argument after node and 'openedx' -const commandName = process.argv[2]; - -// remove 'openedx' from process.argv to allow subcommands to read options properly -process.argv.splice(1, 1); - -switch (commandName) { - case 'eslint': - ensureConfigOption(presets.eslint); - // eslint-disable-next-line import/extensions, import/no-extraneous-dependencies - require('.bin/eslint'); - break; - case 'jest': - ensureConfigOption(presets.jest); - require('jest/bin/jest'); - break; - case 'webpack': - ensureConfigOption(presets.webpack); - require('webpack/bin/webpack'); - break; - case 'webpack-dev-server': - ensureConfigOption(presets.webpackDevServer); - require('webpack-dev-server/bin/webpack-dev-server'); - break; - case 'shell-dev-server': - ensureConfigOption(presets.shellDevServer); - require('webpack-dev-server/bin/webpack-dev-server'); - break; - case 'formatjs': { - const commonArgs = [ - '--format', 'node_modules/@openedx/frontend-base/cli/formatter.js', - '--ignore', 'src/**/*.json', - '--out-file', './temp/formatjs/Default.messages.json', - '--', 'src/**/*.js*', - ]; - process.argv = process.argv.concat(commonArgs); - require('@formatjs/cli/bin/formatjs'); - break; - } - case 'serve': - require('../cli/scripts/serve'); - break; - default: - console.log(chalk.red(`[ERROR] openedx: The command ${chalk.bold.red(commandName)} is unsupported.`)); -} diff --git a/cli/ConfigPreset.js b/cli/ConfigPreset.js deleted file mode 100644 index e71ab2ba..00000000 --- a/cli/ConfigPreset.js +++ /dev/null @@ -1,28 +0,0 @@ -const path = require('path'); -const resolveFilepaths = require('./resolveFilepaths'); - -const defaultConfigDir = path.resolve(__dirname, '../config'); - -function ConfigPreset({ - defaultDir = defaultConfigDir, - defaultFilename, - searchFilenames, - searchFilepaths, -}) { - return { - defaultFilename, - getDefault: () => require(require.resolve(`./${defaultFilename}`, { paths: [defaultDir] })), - get defaultFilepath() { - console.log('getting default filepath', defaultFilename, defaultDir); - return require.resolve(`./${defaultFilename}`, { paths: [defaultDir] }); - }, - get resolvedFilepath() { - return resolveFilepaths( - searchFilenames.map(filename => `./${filename}`), - [...searchFilepaths, defaultDir], - ); - }, - }; -} - -module.exports = ConfigPreset; diff --git a/cli/plugins/html-webpack-new-relic-plugin/index.js b/cli/plugins/html-webpack-new-relic-plugin/index.js deleted file mode 100644 index d296648c..00000000 --- a/cli/plugins/html-webpack-new-relic-plugin/index.js +++ /dev/null @@ -1,3 +0,0 @@ -const HtmlWebpackNewRelicPlugin = require('./HtmlWebpackNewRelicPlugin'); - -module.exports = HtmlWebpackNewRelicPlugin; diff --git a/cli/presets.js b/cli/presets.js deleted file mode 100644 index ef398e67..00000000 --- a/cli/presets.js +++ /dev/null @@ -1,60 +0,0 @@ -const ConfigPreset = require('./ConfigPreset'); - -const searchFilepaths = [process.cwd()]; - -const babel = new ConfigPreset({ - defaultFilename: 'babel.config.js', - searchFilenames: ['.babelrc', '.babelrc.js', 'babel.config.js'], - searchFilepaths, -}); - -const eslint = new ConfigPreset({ - defaultFilename: '.eslintrc.js', - searchFilenames: ['.eslintrc', '.eslintrc.js'], - searchFilepaths, -}); - -const jest = new ConfigPreset({ - defaultFilename: 'jest.config.js', - searchFilenames: ['jest.config.js'], - searchFilepaths, -}); - -const webpackDevServer = new ConfigPreset({ - defaultFilename: 'webpack.dev.config.js', - searchFilenames: ['webpack.dev.config.js'], - searchFilepaths, -}); - -const shellDevServer = new ConfigPreset({ - defaultFilename: 'webpack.shell.dev.config.js', - searchFilenames: ['webpack.shell.dev.config.js'], - searchFilepaths, -}); - -const webpackDevServerStage = new ConfigPreset({ - defaultFilename: 'webpack.dev-stage.config.js', - searchFilenames: ['webpack.dev-stage.config.js'], - searchFilepaths, -}); - -const webpack = new ConfigPreset({ - defaultFilename: 'webpack.prod.config.js', - searchFilenames: ['webpack.prod.config.js'], - searchFilepaths, -}); - -module.exports = { - babel, - eslint, - jest, - webpack, - webpackDevServer, - 'webpack-dev': webpackDevServer, - 'webpack-dev-server': webpackDevServer, - shellDevServer, - webpackDevServerStage, - 'webpack-dev-server-stage': webpackDevServerStage, - 'webpack-dev-stage': webpackDevServerStage, - 'webpack-prod': webpack, -}; diff --git a/cli/resolveFilepaths.js b/cli/resolveFilepaths.js deleted file mode 100644 index c7980e35..00000000 --- a/cli/resolveFilepaths.js +++ /dev/null @@ -1,14 +0,0 @@ -// Resolves a filepath given an array of filepaths and an array of resolvePaths -// (directories to check). Useful for finding files and resolving to defaults -// if they don't exist. -module.exports = (filepaths, resolvePaths = [process.cwd()]) => { - // eslint-disable-next-line no-plusplus - for (let i = 0; i < filepaths.length; i++) { - try { - return require.resolve(filepaths[i], { paths: resolvePaths }); - } catch (e) { - // Do nothing, maybe we'll find it in the next loop - } - } - throw new Error(`Could not resolve files:\n ${filepaths.join('\n')}\n\n in directories:\n ${resolvePaths.join(', ')}\n`); -}; diff --git a/cli/resolvePrivateEnvConfig.js b/cli/resolvePrivateEnvConfig.js deleted file mode 100644 index 8c7532a6..00000000 --- a/cli/resolvePrivateEnvConfig.js +++ /dev/null @@ -1,15 +0,0 @@ -// Allow private/local overrides of env vars from .env.development(-stage) for config settings -// that you'd like to persist locally during development, without the risk of checking -// in temporary modifications to .env.development(-stage). - -const fs = require('fs'); -const dotenv = require('dotenv'); - -module.exports = (filePath) => { - if (fs.existsSync(filePath)) { - const privateEnvConfig = dotenv.parse(fs.readFileSync(filePath)); - Object.entries(privateEnvConfig).forEach(([key, value]) => { - process.env[key] = value; - }); - } -}; diff --git a/config/createConfig.js b/config/createConfig.js deleted file mode 100644 index 09cefbe4..00000000 --- a/config/createConfig.js +++ /dev/null @@ -1,7 +0,0 @@ -const { merge } = require('webpack-merge'); -const getBaseConfig = require('./getBaseConfig'); - -module.exports = (commandName, configFragment = {}) => { - const baseConfig = getBaseConfig(commandName); - return merge(baseConfig, configFragment); -}; diff --git a/config/getBaseConfig.js b/config/getBaseConfig.js deleted file mode 100644 index 5c6983df..00000000 --- a/config/getBaseConfig.js +++ /dev/null @@ -1,9 +0,0 @@ -const presets = require('../cli/presets'); - -module.exports = (commandName) => { - if (presets[commandName] === undefined) { - throw new Error(`openedx: ${commandName} is unsupported in this version`); - } - - return presets[commandName].getDefault(); -}; diff --git a/config/index.js b/config/index.js deleted file mode 100644 index 8224d7b7..00000000 --- a/config/index.js +++ /dev/null @@ -1,7 +0,0 @@ -const createConfig = require('./createConfig'); -const getBaseConfig = require('./getBaseConfig'); - -module.exports = { - createConfig, - getBaseConfig, -}; diff --git a/config/jest/fallback.env.config.js b/config/jest/fallback.env.config.js deleted file mode 100644 index ca3268d5..00000000 --- a/config/jest/fallback.env.config.js +++ /dev/null @@ -1,12 +0,0 @@ -module.exports = { - // DO NOT PUT ANY CONFIGURATION IN THIS FILE. - - // The existence of this file is a technical detail of the mechanism used to implement - // env.config.js files for jest. - - // This file is used as a fallback to prevent build errors if an env.config.js file has not been - // defined in a consuming application. If we could inline an empty object instead of needing a - // file to reference, that'd be clearer, but doesn't seem to be an option. - - // This is NOT an appropriate place to put actual configuration values for tests. -}; diff --git a/config/jest/setupTest.js b/config/jest/setupTest.js deleted file mode 100644 index f1ebf80d..00000000 --- a/config/jest/setupTest.js +++ /dev/null @@ -1,9 +0,0 @@ -const path = require('path'); -const fs = require('fs'); -const dotenv = require('dotenv'); - -const testEnvFile = path.resolve(process.cwd(), '.env.test'); - -if (fs.existsSync(testEnvFile)) { - dotenv.config({ path: testEnvFile }); -} diff --git a/config/webpack.common.config.js b/config/webpack.common.config.js deleted file mode 100644 index 944fdf62..00000000 --- a/config/webpack.common.config.js +++ /dev/null @@ -1,40 +0,0 @@ -const path = require('path'); - -module.exports = { - entry: { - app: path.resolve(process.cwd(), './src/index'), - }, - output: { - path: path.resolve(process.cwd(), './dist'), - publicPath: '/', - }, - resolve: { - alias: { - 'env.config': path.resolve(process.cwd(), './env.config'), - }, - fallback: { - // This causes the system to return an empty object if it can't find an env.config.js file in - // the application being built. - 'env.config': false, - }, - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }, - ignoreWarnings: [ - // Ignore warnings raised by source-map-loader. - // some third party packages may ship miss-configured sourcemaps, that interrupts the build - // See: https://github.com/facebook/create-react-app/discussions/11278#discussioncomment-1780169 - /** - * - * @param {import('webpack').WebpackError} warning - * @returns {boolean} - */ - function ignoreSourcemapsloaderWarnings(warning) { - return ( - warning.module - && warning.module.resource.includes('node_modules') - && warning.details - && warning.details.includes('source-map-loader') - ); - }, - ], -}; diff --git a/config/webpack.dev.config.js b/config/webpack.dev.config.js deleted file mode 100644 index b107dca1..00000000 --- a/config/webpack.dev.config.js +++ /dev/null @@ -1,193 +0,0 @@ -// This is the dev Webpack config. All settings here should prefer a fast build -// time at the expense of creating larger, unoptimized bundles. -const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); - -const { merge } = require('webpack-merge'); -const Dotenv = require('dotenv-webpack'); -const dotenv = require('dotenv'); -const HtmlWebpackPlugin = require('html-webpack-plugin'); -const path = require('path'); -const PostCssAutoprefixerPlugin = require('autoprefixer'); -const PostCssRTLCSS = require('postcss-rtlcss'); -const PostCssCustomMediaCSS = require('postcss-custom-media'); -const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin'); -const { transform } = require('@formatjs/ts-transformer'); - -const commonConfig = require('./webpack.common.config'); -const resolvePrivateEnvConfig = require('../cli/resolvePrivateEnvConfig'); -const getLocalAliases = require('./getLocalAliases'); - -// Add process env vars. Currently used only for setting the -// server port and the publicPath -dotenv.config({ - path: path.resolve(process.cwd(), '.env.development'), -}); - -// Allow private/local overrides of env vars from .env.development for config settings -// that you'd like to persist locally during development, without the risk of checking -// in temporary modifications to .env.development. -resolvePrivateEnvConfig('.env.private'); - -const aliases = getLocalAliases(); -const PUBLIC_PATH = process.env.PUBLIC_PATH || '/'; - -module.exports = merge(commonConfig, { - mode: 'development', - devtool: 'eval-source-map', - output: { - publicPath: PUBLIC_PATH, - }, - resolve: { - alias: aliases, - extensions: ['.js', '.jsx', '.ts', '.tsx'], - }, - module: { - // Specify file-by-file rules to Webpack. Some file-types need a particular kind of loader. - rules: [ - { - test: /\.(js|jsx|ts|tsx)$/, - include: [ - /src/, - ], - use: { - loader: require.resolve('ts-loader'), - options: { - transpileOnly: true, - compilerOptions: { - noEmit: false, - }, - getCustomTransformers() { - return { - before: [ - transform({ - overrideIdFn: '[sha512:contenthash:base64:6]', - }), - ], - }; - }, - }, - }, - }, - // We are not extracting CSS from the javascript bundles in development because extracting - // prevents hot-reloading from working, it increases build time, and we don't care about - // flash-of-unstyled-content issues in development. - { - test: /(.scss|.css)$/, - use: [ - require.resolve('style-loader'), // creates style nodes from JS strings - { - loader: require.resolve('css-loader'), // translates CSS into CommonJS - options: { - sourceMap: true, - modules: { - compileType: 'icss', - }, - }, - }, - { - loader: require.resolve('postcss-loader'), - options: { - postcssOptions: { - plugins: [ - PostCssAutoprefixerPlugin(), - PostCssRTLCSS(), - PostCssCustomMediaCSS(), - ], - }, - }, - }, - require.resolve('resolve-url-loader'), - { - loader: require.resolve('sass-loader'), // compiles Sass to CSS - options: { - sourceMap: true, - sassOptions: { - includePaths: [ - path.join(process.cwd(), 'node_modules'), - path.join(process.cwd(), 'src'), - ], - // silences compiler warnings regarding deprecation warnings - quietDeps: true, - }, - }, - }, - ], - }, - // Webpack, by default, uses the url-loader for images and fonts that are required/included by - // files it processes, which just base64 encodes them and inlines them in the javascript - // bundles. This makes the javascript bundles ginormous and defeats caching so we will use the - // file-loader instead to copy the files directly to the output directory. - { - test: /\.(woff2?|ttf|svg|eot)(\?v=\d+\.\d+\.\d+)?$/, - loader: require.resolve('file-loader'), - }, - { - test: /favicon.ico$/, - loader: require.resolve('file-loader'), - options: { - name: '[name].[ext]', // <-- retain original file name - }, - }, - { - test: /\.(jpe?g|png|gif)(\?v=\d+\.\d+\.\d+)?$/, - loader: require.resolve('file-loader'), - }, - ], - }, - optimization: { - minimizer: [ - '...', - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.sharpMinify, - options: { - encodeOptions: { - ...['png', 'jpeg', 'jpg'].reduce((accumulator, value) => ( - { ...accumulator, [value]: { progressive: true, quality: 65 } } - ), {}), - gif: { - effort: 5, - }, - }, - }, - }, - }), - ], - }, - // Specify additional processing or side-effects done on the Webpack output bundles as a whole. - plugins: [ - // Generates an HTML file in the output directory. - new HtmlWebpackPlugin({ - inject: true, // Appends script tags linking to the webpack bundles at the end of the body - template: path.resolve(process.cwd(), 'public/index.html'), - FAVICON_URL: process.env.FAVICON_URL || null, - OPTIMIZELY_PROJECT_ID: process.env.OPTIMIZELY_PROJECT_ID || null, - NODE_ENV: process.env.NODE_ENV || null, - }), - new Dotenv({ - path: path.resolve(process.cwd(), '.env.development'), - systemvars: true, - }), - new ReactRefreshWebpackPlugin(), - ], - // This configures webpack-dev-server which serves bundles from memory and provides live - // reloading. - devServer: { - host: '0.0.0.0', - port: process.env.PORT || 8080, - historyApiFallback: { - index: path.join(PUBLIC_PATH, 'index.html'), - disableDotRule: true, - }, - // Enable hot reloading server. It will provide WDS_SOCKET_PATH endpoint - // for the WebpackDevServer client so it can learn when the files were - // updated. The WebpackDevServer client is included as an entry point - // in the webpack development configuration. Note that only changes - // to CSS are currently hot reloaded. JS changes will refresh the browser. - hot: true, - webSocketServer: 'ws', - devMiddleware: { - publicPath: PUBLIC_PATH, - }, - }, -}); diff --git a/docs/decisions/0002-js-environment-config.md b/docs/decisions/0002-js-environment-config.md index 1d0245db..d84754b6 100644 --- a/docs/decisions/0002-js-environment-config.md +++ b/docs/decisions/0002-js-environment-config.md @@ -28,20 +28,20 @@ We will still support `process.env`-based configuration for the time being. Tod # Implementation -This new mechanism is done via webpack's `resolve` rules, specifically `alias` and `fallback`. We use the first rule to set up an alias for `"env.config"` and point it at a `env.config.js` file we expect to be in the root of the application being built with frontend-build. If that file is not present, `fallback` is responsible for resolving it with an empty `env.config.js` file here in frontend-build, which provides an empty object of configuration values. +This new mechanism is done via webpack's `resolve` rules, specifically `alias` and `fallback`. We use the first rule to set up an alias for `"site.config"` and point it at a `site.config.tsx` file we expect to be in the root of the application being built with frontend-build. If that file is not present, `fallback` is responsible for resolving it with an empty `site.config.tsx` file here in frontend-build, which provides an empty object of configuration values. -In this way, if an application doesn't know about or doesn't use `env.config.js`, it will seamlessly fallback to be a no-op. +In this way, if an application doesn't know about or doesn't use `site.config.tsx`, it will seamlessly fallback to be a no-op. Once in place, an application can do: ``` -import config from 'env.config'; +import config from 'site.config'; ``` -And it'll import the code from `env.config.js` if it's present, or the frontend-build version (which is an empty object `{}`) if it is not. +And it'll import the code from `site.config.tsx` if it's present, or the frontend-build version (which is an empty object `{}`) if it is not. # Follow-on work -After getting this code committed to frontend-build, we'll want to consume it in frontend-platform. This will probably take the form of a new "configuration service" interface with two implementations: the existing `process.env`-based implementation packaged into a class, and a new `env.config`-based implementation. Cutting over from one to the other over time will have to be done in a backwards compatible way so that we can continue to support existing MFE builds. +After getting this code committed to frontend-build, we'll want to consume it in frontend-platform. This will probably take the form of a new "configuration service" interface with two implementations: the existing `process.env`-based implementation packaged into a class, and a new `site.config`-based implementation. Cutting over from one to the other over time will have to be done in a backwards compatible way so that we can continue to support existing MFE builds. We expect that we'll also want to update https://github.com/openedx/tubular to support this new mechanism. Operators who want to take advantage of it may need to update any repositories that contain their environment-specific configurations as well. diff --git a/docs/decisions/0003-fedx-scripts-serve.md b/docs/decisions/0003-fedx-scripts-serve.md index e0990f10..4783c7ea 100644 --- a/docs/decisions/0003-fedx-scripts-serve.md +++ b/docs/decisions/0003-fedx-scripts-serve.md @@ -16,9 +16,9 @@ To mitigate this, this ADR describes a new mechanism to provide a standard, docu # Decision -We will create a new `serve` command for `openedx` that creates an Express.js server to run the generated `dist` file assets (e.g., `index.html`) on the `PORT` specified in the MFE's `env.config.js` and/or `.env.development|private` file(s) on `localhost`. +We will create a new `serve` command for `openedx` that creates an Express.js server to run the generated `dist` file assets (e.g., `index.html`) on the `PORT` specified in the MFE's `site.config.tsx` and/or `.env.development|private` file(s) on `localhost`. -If no `env.config.js` and/or `.env.development|private` file(s) exist and/or no `PORT` setting is specified those files, the `serve` command will fallback to a default port 8080, which is similar to the default ports our typical example MFE applications use. +If no `site.config.tsx` and/or `.env.development|private` file(s) exist and/or no `PORT` setting is specified those files, the `serve` command will fallback to a default port 8080, which is similar to the default ports our typical example MFE applications use. # Implementation @@ -34,7 +34,7 @@ Once in place, a MFE application may add a `serve` script to its NPM scripts in } ``` -Then, running `npm run serve` in the root of that MFE application will run the new `serve` command in `@edx/frontend-build`, serving the assets in the MFE's `dist` directory on the `PORT` specified in the `env.config.js` file or `.env.development|private` file(s). +Then, running `npm run serve` in the root of that MFE application will run the new `serve` command in `@edx/frontend-build`, serving the assets in the MFE's `dist` directory on the `PORT` specified in the `site.config.tsx` file or `.env.development|private` file(s). # Change Log diff --git a/docs/decisions/0007-javascript-file-configuration.rst b/docs/decisions/0007-javascript-file-configuration.rst index 9de22593..328eea0a 100644 --- a/docs/decisions/0007-javascript-file-configuration.rst +++ b/docs/decisions/0007-javascript-file-configuration.rst @@ -72,7 +72,7 @@ Decision For the above reasons, we will deprecate environment variable configuration in favor of JavaScript file configuration. -This method makes use of an ``env.config.js`` file to supply configuration +This method makes use of an ``site.config.tsx`` file to supply configuration variables to an application:: const config = { diff --git a/docs/decisions/0008-stylesheet-import-in-env-config.md b/docs/decisions/0008-stylesheet-import-in-site-config.md similarity index 68% rename from docs/decisions/0008-stylesheet-import-in-env-config.md rename to docs/decisions/0008-stylesheet-import-in-site-config.md index 42299b54..eba07011 100644 --- a/docs/decisions/0008-stylesheet-import-in-env-config.md +++ b/docs/decisions/0008-stylesheet-import-in-site-config.md @@ -1,20 +1,20 @@ -# Shell stylesheet must be imported in env.config file. +# Shell stylesheet must be imported in site.config file. ## Summary -A project must import the stylesheet for the application shell in its env.config file. +A project must import the stylesheet for the application shell in its site.config file. ## Context There is a particular quirk of the stylesheet loaders for webpack (style-loader and/or css-loader) where the import of stylesheets into JavaScript files must take place in a JS file in the project, not in library dependency like frontend-base. Further, the stylesheet imported into JS must _itself_ be a part of the project. -If, for instance, we try to import a stylesheet from frontend-base (shell, header, footer, etc.) inside a React component inside the shell, webpack silently ignores the import and refuses to load the stylesheet. If we try to import a stylesheet from frontend-base directly into the env.config file in the project, that will also fail with webpack silently ignoring the stylesheet. If, however, frontend-base exports the stylesheet and it's loaded into a SCSS file in the project and _that_ is imported into env.config, everything works correctly. +If, for instance, we try to import a stylesheet from frontend-base (shell, header, footer, etc.) inside a React component inside the shell, webpack silently ignores the import and refuses to load the stylesheet. If we try to import a stylesheet from frontend-base directly into the site.config file in the project, that will also fail with webpack silently ignoring the stylesheet. If, however, frontend-base exports the stylesheet and it's loaded into a SCSS file in the project and _that_ is imported into site.config, everything works correctly. -This slight indirection through a SCSS file in the project is necessary, and arguably desirable. It ensure as common, unified entry point for SCSS from dependencies of the project. SCSS from the project or micro-frontend itself can be imported into its own components, or can be imported into this top-level SCSS file as desired. Further, this ensures that every aspect of the style of a project or MFE can easily be customized since the stylesheet is supplied through the env.config file. +This slight indirection through a SCSS file in the project is necessary, and arguably desirable. It ensure as common, unified entry point for SCSS from dependencies of the project. SCSS from the project or micro-frontend itself can be imported into its own components, or can be imported into this top-level SCSS file as desired. Further, this ensures that every aspect of the style of a project or MFE can easily be customized since the stylesheet is supplied through the site.config file. ## Decision -As a best practice, a project should have a top-level SCSS file as a peer to the env.config file. This SCSS file should import the stylesheet from the frontend-base shell application. It should, in turn, be imported into the env.config file. +As a best practice, a project should have a top-level SCSS file as a peer to the site.config file. This SCSS file should import the stylesheet from the frontend-base shell application. It should, in turn, be imported into the site.config file. ## Implementation @@ -26,7 +26,7 @@ The `project.scss` file should import the stylesheet from the shell: // other styles ``` -The env.config file should then import the top-level SCSS file: +The site.config file should then import the top-level SCSS file: ```diff + import './project.scss'; diff --git a/example-plugin-app/env.config.js b/example-plugin-app/site.config.tsx similarity index 94% rename from example-plugin-app/env.config.js rename to example-plugin-app/site.config.tsx index bbcb3de3..693a92ba 100644 --- a/example-plugin-app/env.config.js +++ b/example-plugin-app/site.config.tsx @@ -17,7 +17,6 @@ const config = { STUDIO_BASE_URL: 'http://localhost:18010', MARKETING_SITE_BASE_URL: 'http://localhost:18000', ORDER_HISTORY_URL: 'http://localhost:1996/orders', - REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://localhost:18000/login_refresh', SEGMENT_KEY: null, SITE_NAME: 'localhost', USER_INFO_COOKIE_NAME: 'edx-user-info', diff --git a/example/env.config.jsx b/example/site.config.tsx similarity index 98% rename from example/env.config.jsx rename to example/site.config.tsx index 934353b2..9944e473 100644 --- a/example/env.config.jsx +++ b/example/site.config.tsx @@ -46,7 +46,6 @@ const config = { STUDIO_BASE_URL: 'http://localhost:18010', MARKETING_SITE_BASE_URL: 'http://localhost:18000', ORDER_HISTORY_URL: 'http://localhost:1996/orders', - REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://localhost:18000/login_refresh', SEGMENT_KEY: null, SITE_NAME: 'localhost', USER_INFO_COOKIE_NAME: 'edx-user-info', diff --git a/frontend-base.d.ts b/frontend-base.d.ts index b2db15f7..f80647da 100644 --- a/frontend-base.d.ts +++ b/frontend-base.d.ts @@ -1,9 +1,7 @@ -/// +import { SiteConfig } from "./"; -import { OpenedXConfig } from "./dist"; - -declare module 'env.config' { - export default OpenedXConfig; +declare module 'site.config' { + export default SiteConfig; } declare module '*.svg' { diff --git a/index.ts b/index.ts index 59702a32..7959db4c 100644 --- a/index.ts +++ b/index.ts @@ -49,8 +49,6 @@ export { createIntl, defineMessages, ensureAuthenticatedUser, - ensureConfig, - ensureDefinedConfig, fetchAuthenticatedUser, getAnalyticsService, getAuthService, @@ -59,6 +57,7 @@ export { getConfig, getCountryList, getCountryMessages, + getHistory, getHttpClient, getLanguageList, getLanguageMessages, @@ -71,7 +70,6 @@ export { getPrimaryLanguageSubtag, getQueryParameters, handleRtl, - history, hydrateAuthenticatedUser, identifyAnonymousUser, identifyAuthenticatedUser, @@ -105,11 +103,10 @@ export { useIntl } from './runtime'; -export interface OpenedXConfig { - /** - * The application content for the shell. This is a temporary configuration option while we - * convert micro-frontends to plugins. It must be a React functional component that takes no - * arguments and returns JSX. - */ - app?: () => JSX.Element -} +export type { + ExternalAppConfig, + FederatedAppConfig, + InternalAppConfig, + SiteConfig, + ModuleConfig +} from './types'; diff --git a/jest.config.js b/jest.config.js index 841f2785..a58c881b 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,3 +1,3 @@ -const { createConfig } = require('./config'); +const { createConfig } = require('./tools'); module.exports = createConfig('jest'); diff --git a/package-lock.json b/package-lock.json index 846cc5c4..3d2e6c84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,9 +19,10 @@ "@formatjs/intl-pluralrules": "^4.3.3", "@formatjs/intl-relativetimeformat": "^10.0.1", "@formatjs/ts-transformer": "^3.13.14", - "@fullhuman/postcss-purgecss": "5.0.0", + "@module-federation/enhanced": "^0.4.0", "@module-federation/runtime": "^0.2.6", "@pmmmwh/react-refresh-webpack-plugin": "0.5.15", + "@types/gradient-string": "^1.1.6", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "autoprefixer": "10.4.19", @@ -32,10 +33,9 @@ "chalk": "4.1.2", "classnames": "^2.5.1", "clean-webpack-plugin": "4.0.0", + "compression": "^1.7.4", "css-loader": "5.2.7", "cssnano": "6.0.3", - "dotenv": "8.6.0", - "dotenv-webpack": "8.0.1", "eslint": "8.44.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "^17.0.0", @@ -48,6 +48,7 @@ "file-loader": "6.2.0", "form-urlencoded": "^6.1.5", "glob": "^7.2.3", + "gradient-string": "^2.0.2", "history": "^4.10.1", "html-webpack-plugin": "5.6.0", "i18n-iso-countries": "^4.3.1", @@ -91,16 +92,21 @@ "webpack-merge": "^5.10.0" }, "bin": { - "intl-imports.js": "dist/cli/scripts/intl-imports.js", - "openedx": "dist/bin/openedx.js", - "transifex-utils.js": "dist/cli/scripts/transifex-utils.js" + "intl-imports.js": "dist/tools/cli/scripts/intl-imports.js", + "openedx": "dist/tools/cli/openedx.js", + "transifex-utils.js": "dist/tools/cli/scripts/transifex-utils.js" }, "devDependencies": { "@testing-library/dom": "^8.20.1", "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^8.0.1", + "@tsconfig/node18": "^18.2.4", + "@types/compression": "^1.7.5", "@types/jest": "^29.5.12", + "@types/lodash.camelcase": "^4.3.9", + "@types/lodash.merge": "^4.6.9", + "@types/node": "^18.19.43", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.11", "axios-mock-adapter": "^1.22.0", @@ -114,7 +120,8 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.3", - "react-router-dom": "^6.24.1", + "react-router": "^6.26.1", + "react-router-dom": "^6.26.1", "redux": "^4.2.1" } }, @@ -149,28 +156,28 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -186,11 +193,11 @@ } }, "node_modules/@babel/generator": { - "version": "7.24.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.10.tgz", - "integrity": "sha512-o9HBZL1G2129luEUlG1hB4N/nlYNWHnpwlND9eOMclRqqu1YDy2sSYVCFUZwl8I1Gxh+QSRrP2vD7EpUmFVXxg==", + "version": "7.25.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.5.tgz", + "integrity": "sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==", "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.1" @@ -223,11 +230,11 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dependencies": { - "@babel/compat-data": "^7.24.8", + "@babel/compat-data": "^7.25.2", "@babel/helper-validator-option": "^7.24.8", "browserslist": "^4.23.1", "lru-cache": "^5.1.1", @@ -238,18 +245,16 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.24.8.tgz", - "integrity": "sha512-4f6Oqnmyp2PP3olgUMmOwC3akxSm5aBYraQ6YDdKy7NcAMkDECHWG0DEnV6M2UAkERgIBhYt8S27rURPg7SxWA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.4.tgz", + "integrity": "sha512-ro/bFs3/84MDgDmMwbcHgDa8/E6J3QKNTk4xJJnVeFtGE+tL0K26E3pNxhYz2b67fJpt7Aphw5XcploKXuCvCQ==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", "@babel/helper-member-expression-to-functions": "^7.24.8", "@babel/helper-optimise-call-expression": "^7.24.7", - "@babel/helper-replace-supers": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/traverse": "^7.25.4", "semver": "^6.3.1" }, "engines": { @@ -260,9 +265,9 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.24.7.tgz", - "integrity": "sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.2.tgz", + "integrity": "sha512-+wqVGP+DFmqwFD3EH6TMTfUNeqDehV3E/dl+Sd54eaXqm17tEUNbEIn4sVivVowbvUpOtIGxdo3GoXyDH9N/9g==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "regexpu-core": "^5.3.1", @@ -290,40 +295,6 @@ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-member-expression-to-functions": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.24.8.tgz", @@ -349,15 +320,14 @@ } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -386,13 +356,13 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.24.7.tgz", - "integrity": "sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.0.tgz", + "integrity": "sha512-NhavI2eWEIz/H9dbrG0TuOicDhNexze43i5z7lEqwYm0WEZVTwnPpA0EafUTP7+6/W79HWIP2cTe3Z5NiSTVpw==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-wrap-function": "^7.24.7" + "@babel/helper-wrap-function": "^7.25.0", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -402,13 +372,13 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.24.7.tgz", - "integrity": "sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.0.tgz", + "integrity": "sha512-q688zIvQVYtZu+i2PsdIu/uWGRpfxzr5WESsfpShfZECkO+d2o+WROWezCi/Q6kJ0tfPa5+pUGUlfx2HhrA3Bg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-member-expression-to-functions": "^7.24.7", - "@babel/helper-optimise-call-expression": "^7.24.7" + "@babel/helper-member-expression-to-functions": "^7.24.8", + "@babel/helper-optimise-call-expression": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -441,17 +411,6 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", - "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-string-parser": { "version": "7.24.8", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", @@ -477,26 +436,25 @@ } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.24.7.tgz", - "integrity": "sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.0.tgz", + "integrity": "sha512-s6Q1ebqutSiZnEjaofc/UKDyC4SbzV5n5SrA2Gq8UawLycr3i04f1dX4OzoQVnexm6aOCh37SQNYlJ/8Ku+PMQ==", "dependencies": { - "@babel/helper-function-name": "^7.24.7", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.25.0.tgz", + "integrity": "sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -581,9 +539,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", - "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.25.4.tgz", + "integrity": "sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==", + "dependencies": { + "@babel/types": "^7.25.4" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -592,12 +553,26 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.24.7.tgz", - "integrity": "sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==", + "version": "7.25.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.3.tgz", + "integrity": "sha512-wUrcsxZg6rqBXG05HG1FPYgsP6EvwF4WpBbxIpWIIYnH8wG0gzx3yZY3dtEHas4sTAOGkbTsc9EGPxwff8lRoA==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.0.tgz", + "integrity": "sha512-Bm4bH2qsX880b/3ziJ8KD711LT7z4u8CFudmjqle65AZj/HNUFhEf90dqYv6O86buWvSBmeQDjv0Tn2aF/bIBA==", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -607,11 +582,11 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.24.7.tgz", - "integrity": "sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.0.tgz", + "integrity": "sha512-lXwdNZtTmeVOOFtwM/WDe7yg1PL8sYhRk/XH0FzbR2HDQ0xC+EnQ/JHeoMYSavtU115tnUk0q9CDyq8si+LMAA==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -637,12 +612,12 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.24.7.tgz", - "integrity": "sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.0.tgz", + "integrity": "sha512-tggFrk1AIShG/RUQbEwt2Tr/E+ObkfwrPjR6BjbRvsx24+PSjK8zrq0GWPNCjo8qpRx4DuJzlcvWJqlm+0h3kw==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -890,11 +865,11 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -933,14 +908,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.24.7.tgz", - "integrity": "sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.25.4.tgz", + "integrity": "sha512-jz8cV2XDDTqjKPwVPJBIjORVEmSGYhdRa8e5k5+vN+uwcjSrSxUaebBRa4ko1jqNF2uxyg8G6XYk30Jv285xzg==", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-remap-async-to-generator": "^7.24.7", - "@babel/plugin-syntax-async-generators": "^7.8.4" + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-remap-async-to-generator": "^7.25.0", + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/traverse": "^7.25.4" }, "engines": { "node": ">=6.9.0" @@ -980,11 +955,11 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.24.7.tgz", - "integrity": "sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.0.tgz", + "integrity": "sha512-yBQjYoOjXlFv9nlXb3f1casSHOZkWr29NX+zChVanLg5Nc157CrbEX9D7hxxtTpuFy7Q0YzmmWfJxzvps4kXrQ==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -994,12 +969,12 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.24.7.tgz", - "integrity": "sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.4.tgz", + "integrity": "sha512-nZeZHyCWPfjkdU5pA/uHiTaDAFUEqkpzf1YoQT2NeSynCGYq9rxfyI3XpQbfx/a0hSnFH6TGlEXvae5Vi7GD8g==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1025,17 +1000,15 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.24.8.tgz", - "integrity": "sha512-VXy91c47uujj758ud9wx+OMgheXm4qJfyhj1P18YvlrQkNOSrwsteHk+EFS3OMGfhMhpZa0A+81eE7G4QC+3CA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.4.tgz", + "integrity": "sha512-oexUfaQle2pF/b6E0dwsxQtAol9TLSO88kQvym6HHBWFliV2lGdrPieX+WgMRLSJDVzdYywk7jXbLPuO2KLTLg==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", - "@babel/helper-replace-supers": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", + "@babel/helper-replace-supers": "^7.25.0", + "@babel/traverse": "^7.25.4", "globals": "^11.1.0" }, "engines": { @@ -1103,6 +1076,21 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.0.tgz", + "integrity": "sha512-YLpb4LlYSc3sCUa35un84poXoraOiQucUTTu8X1j18JV+gNa8E0nyUf/CjZ171IRGr4jEguF+vzJU66QZhn29g==", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, "node_modules/@babel/plugin-transform-dynamic-import": { "version": "7.24.7", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.24.7.tgz", @@ -1164,13 +1152,13 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.24.7.tgz", - "integrity": "sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==", + "version": "7.25.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.1.tgz", + "integrity": "sha512-TVVJVdW9RKMNgJJlLtHsKDTydjZAbwIsn6ySBPQaEAUU5+gVvlJt/9nRmqVbsV/IBanRjzWoaAQKLoamWVOUuA==", "dependencies": { - "@babel/helper-compilation-targets": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-compilation-targets": "^7.24.8", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/traverse": "^7.25.1" }, "engines": { "node": ">=6.9.0" @@ -1195,11 +1183,11 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.24.7.tgz", - "integrity": "sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.2.tgz", + "integrity": "sha512-HQI+HcTbm9ur3Z2DkO+jgESMAMcYLuN/A7NRw9juzxAezN9AvqvUTnpKP/9kkYANz6u7dFlAyOu44ejuGySlfw==", "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1269,14 +1257,14 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.24.7.tgz", - "integrity": "sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.0.tgz", + "integrity": "sha512-YPJfjQPDXxyQWg/0+jHKj1llnY5f/R6a0p/vP4lPymxLu7Lvl4k2WMitqi08yxwQcCVUUdG9LCUj4TNEgAp3Jw==", "dependencies": { - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-module-transforms": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "@babel/helper-module-transforms": "^7.25.0", + "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.0" }, "engines": { "node": ">=6.9.0" @@ -1437,12 +1425,12 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.24.7.tgz", - "integrity": "sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.4.tgz", + "integrity": "sha512-ao8BG7E2b/URaUQGqN3Tlsg+M3KlHY6rJ1O1gXAEUnZoyNQnvKyH87Kfg+FoxSeyWUB8ISZZsC91C44ZuBFytw==", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-class-features-plugin": "^7.25.4", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1497,15 +1485,15 @@ } }, "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.24.7.tgz", - "integrity": "sha512-+Dj06GDZEFRYvclU6k4bme55GKBEWUmByM/eoKuqg4zTNQHiApWRhQph5fxQB2wAEFvRzL1tOEj1RJ19wJrhoA==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.2.tgz", + "integrity": "sha512-KQsqEAVBpU82NM/B/N9j9WOdphom1SZH3R+2V7INrQUH+V9EBFwZsEJl8eBIVeQE62FxJCc70jzEZwqU7RcVqA==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7", + "@babel/helper-plugin-utils": "^7.24.8", "@babel/plugin-syntax-jsx": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/types": "^7.25.2" }, "engines": { "node": ">=6.9.0" @@ -1644,13 +1632,14 @@ } }, "node_modules/@babel/plugin-transform-typescript": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.24.8.tgz", - "integrity": "sha512-CgFgtN61BbdOGCP4fLaAMOPkzWUh6yQZNMr5YSt8uz2cZSSiQONCQFWqsE4NeVfOIhqDOlS9CR3WD91FzMeB2Q==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.25.2.tgz", + "integrity": "sha512-lBwRvjSmqiMYe/pS0+1gggjJleUJi7NzjvQ1Fkqtt69hBa/0t1YuW/MLQMAPixfwaQOHUXsd6jeU3Z+vdGv3+A==", "dependencies": { "@babel/helper-annotate-as-pure": "^7.24.7", - "@babel/helper-create-class-features-plugin": "^7.24.8", + "@babel/helper-create-class-features-plugin": "^7.25.0", "@babel/helper-plugin-utils": "^7.24.8", + "@babel/helper-skip-transparent-expression-wrappers": "^7.24.7", "@babel/plugin-syntax-typescript": "^7.24.7" }, "engines": { @@ -1705,12 +1694,12 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.24.7.tgz", - "integrity": "sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.4.tgz", + "integrity": "sha512-qesBxiWkgN1Q+31xUE9RcMk79eOXXDCv6tfyGMRSs4RGlioSg2WVyQAm07k726cSE56pa+Kb0y9epX2qaXzTvA==", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.24.7", - "@babel/helper-plugin-utils": "^7.24.7" + "@babel/helper-create-regexp-features-plugin": "^7.25.2", + "@babel/helper-plugin-utils": "^7.24.8" }, "engines": { "node": ">=6.9.0" @@ -1720,18 +1709,19 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.24.8.tgz", - "integrity": "sha512-vObvMZB6hNWuDxhSaEPTKCwcqkAIuDtE+bQGn4XMXne1DSLzFVY8Vmj1bm+mUQXYNN8NmaQEO+r8MMbzPr1jBQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.25.4.tgz", + "integrity": "sha512-W9Gyo+KmcxjGahtt3t9fb14vFRWvPpu5pT6GBlovAK6BTBcxgjfVMSQCfJl4oi35ODrxP6xx2Wr8LNST57Mraw==", "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-compilation-targets": "^7.24.8", + "@babel/compat-data": "^7.25.4", + "@babel/helper-compilation-targets": "^7.25.2", "@babel/helper-plugin-utils": "^7.24.8", "@babel/helper-validator-option": "^7.24.8", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.24.7", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.24.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.3", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.0", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.0", "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.24.7", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.24.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.0", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-class-properties": "^7.12.13", @@ -1752,29 +1742,30 @@ "@babel/plugin-syntax-top-level-await": "^7.14.5", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", "@babel/plugin-transform-arrow-functions": "^7.24.7", - "@babel/plugin-transform-async-generator-functions": "^7.24.7", + "@babel/plugin-transform-async-generator-functions": "^7.25.4", "@babel/plugin-transform-async-to-generator": "^7.24.7", "@babel/plugin-transform-block-scoped-functions": "^7.24.7", - "@babel/plugin-transform-block-scoping": "^7.24.7", - "@babel/plugin-transform-class-properties": "^7.24.7", + "@babel/plugin-transform-block-scoping": "^7.25.0", + "@babel/plugin-transform-class-properties": "^7.25.4", "@babel/plugin-transform-class-static-block": "^7.24.7", - "@babel/plugin-transform-classes": "^7.24.8", + "@babel/plugin-transform-classes": "^7.25.4", "@babel/plugin-transform-computed-properties": "^7.24.7", "@babel/plugin-transform-destructuring": "^7.24.8", "@babel/plugin-transform-dotall-regex": "^7.24.7", "@babel/plugin-transform-duplicate-keys": "^7.24.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.0", "@babel/plugin-transform-dynamic-import": "^7.24.7", "@babel/plugin-transform-exponentiation-operator": "^7.24.7", "@babel/plugin-transform-export-namespace-from": "^7.24.7", "@babel/plugin-transform-for-of": "^7.24.7", - "@babel/plugin-transform-function-name": "^7.24.7", + "@babel/plugin-transform-function-name": "^7.25.1", "@babel/plugin-transform-json-strings": "^7.24.7", - "@babel/plugin-transform-literals": "^7.24.7", + "@babel/plugin-transform-literals": "^7.25.2", "@babel/plugin-transform-logical-assignment-operators": "^7.24.7", "@babel/plugin-transform-member-expression-literals": "^7.24.7", "@babel/plugin-transform-modules-amd": "^7.24.7", "@babel/plugin-transform-modules-commonjs": "^7.24.8", - "@babel/plugin-transform-modules-systemjs": "^7.24.7", + "@babel/plugin-transform-modules-systemjs": "^7.25.0", "@babel/plugin-transform-modules-umd": "^7.24.7", "@babel/plugin-transform-named-capturing-groups-regex": "^7.24.7", "@babel/plugin-transform-new-target": "^7.24.7", @@ -1785,7 +1776,7 @@ "@babel/plugin-transform-optional-catch-binding": "^7.24.7", "@babel/plugin-transform-optional-chaining": "^7.24.8", "@babel/plugin-transform-parameters": "^7.24.7", - "@babel/plugin-transform-private-methods": "^7.24.7", + "@babel/plugin-transform-private-methods": "^7.25.4", "@babel/plugin-transform-private-property-in-object": "^7.24.7", "@babel/plugin-transform-property-literals": "^7.24.7", "@babel/plugin-transform-regenerator": "^7.24.7", @@ -1798,10 +1789,10 @@ "@babel/plugin-transform-unicode-escapes": "^7.24.7", "@babel/plugin-transform-unicode-property-regex": "^7.24.7", "@babel/plugin-transform-unicode-regex": "^7.24.7", - "@babel/plugin-transform-unicode-sets-regex": "^7.24.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.25.4", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.10.4", + "babel-plugin-polyfill-corejs3": "^0.10.6", "babel-plugin-polyfill-regenerator": "^0.6.1", "core-js-compat": "^3.37.1", "semver": "^6.3.1" @@ -1869,9 +1860,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.8.tgz", - "integrity": "sha512-5F7SDGs1T72ZczbRwbGO9lQi0NLjQxzl6i4lJxLxfW9U5UluCSyEJeniWvnhl3/euNiqQVbo8zruhsDfid0esA==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.4.tgz", + "integrity": "sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -1880,9 +1871,9 @@ } }, "node_modules/@babel/runtime-corejs3": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.24.8.tgz", - "integrity": "sha512-DXG/BhegtMHhnN7YPIvxWd303/9aXvYFD1TjNL3CD6tUrhI2LVsg3Lck0aql5TRH29n4sj3emcROypkZVUfSuA==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.25.0.tgz", + "integrity": "sha512-BOehWE7MgQ8W8Qn0CQnMtg2tHPHPulcS/5AVpFvs2KCK1ET+0WqZqPvnpRpFN81gYoFopdIEJX9Sgjw3ZBccPg==", "peer": true, "dependencies": { "core-js-pure": "^3.30.2", @@ -1893,31 +1884,28 @@ } }, "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.0.tgz", + "integrity": "sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" + "@babel/parser": "^7.25.0", + "@babel/types": "^7.25.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.4.tgz", + "integrity": "sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==", "dependencies": { "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", + "@babel/generator": "^7.25.4", + "@babel/parser": "^7.25.4", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.4", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1926,9 +1914,9 @@ } }, "node_modules/@babel/types": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", - "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.25.4.tgz", + "integrity": "sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==", "dependencies": { "@babel/helper-string-parser": "^7.24.8", "@babel/helper-validator-identifier": "^7.24.7", @@ -2099,6 +2087,15 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -2124,6 +2121,17 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -2403,6 +2411,11 @@ } } }, + "node_modules/@formatjs/ts-transformer/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.6.0", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.6.0.tgz", @@ -2437,17 +2450,6 @@ "react": ">=16.x" } }, - "node_modules/@fullhuman/postcss-purgecss": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@fullhuman/postcss-purgecss/-/postcss-purgecss-5.0.0.tgz", - "integrity": "sha512-onDS/b/2pMRzqSoj4qOs2tYFmOpaspjTAgvACIHMPiicu1ptajiBruTrjBzTKdxWdX0ldaBb7wj8nEaTLyFkJw==", - "dependencies": { - "purgecss": "^5.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -2462,6 +2464,26 @@ "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -2891,6 +2913,158 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==" }, + "node_modules/@module-federation/bridge-react-webpack-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/bridge-react-webpack-plugin/-/bridge-react-webpack-plugin-0.4.0.tgz", + "integrity": "sha512-su/ZpRZcyZ8yVa5+zmZyh2pW/BdCQsto52Xmp75GRgPgwG7IrczcQ/GlMCHZz9hQQdmLEQuPe4BIvpnH+GS9Jw==", + "dependencies": { + "@module-federation/sdk": "0.4.0" + } + }, + "node_modules/@module-federation/dts-plugin": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/dts-plugin/-/dts-plugin-0.4.0.tgz", + "integrity": "sha512-KCFIdUh6PhoNqU8OlLmbhjec+wWyo9HAUukR84TnjiktVYMcCqmlmSeWoma2llyPE3GeOHqln+cPR7hMPXbR6w==", + "dependencies": { + "@module-federation/managers": "0.4.0", + "@module-federation/sdk": "0.4.0", + "@module-federation/third-party-dts-extractor": "0.4.0", + "adm-zip": "^0.5.10", + "ansi-colors": "^4.1.3", + "axios": "^1.6.7", + "chalk": "3.0.0", + "fs-extra": "9.1.0", + "isomorphic-ws": "5.0.0", + "koa": "2.15.3", + "lodash.clonedeepwith": "4.5.0", + "log4js": "6.9.1", + "node-schedule": "2.1.1", + "rambda": "^9.1.0", + "ws": "8.17.1" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/axios": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.5.tgz", + "integrity": "sha512-fZu86yCo+svH3uqJ/yTdQ0QHpQu5oL+/QE+QPSv6BZSkDAoky9vytxp7u5qk83OJFS3kEBcesWni9WTZAv3tSw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/@module-federation/dts-plugin/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/enhanced": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/enhanced/-/enhanced-0.4.0.tgz", + "integrity": "sha512-F398RJioMiU4zpXjtF+bJG7sROGNnY1dyPxwfhAnfDxtm/5urWnR2od2+zTvucyWdNeWQxbEe9XUOpgt42egqw==", + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "0.4.0", + "@module-federation/dts-plugin": "0.4.0", + "@module-federation/managers": "0.4.0", + "@module-federation/manifest": "0.4.0", + "@module-federation/rspack": "0.4.0", + "@module-federation/runtime-tools": "0.4.0", + "@module-federation/sdk": "0.4.0", + "btoa": "^1.2.1", + "upath": "2.0.1" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@module-federation/managers": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/managers/-/managers-0.4.0.tgz", + "integrity": "sha512-c4apAaQjwR01qlDSNwPfBBTTzVyErO6POayaRIeAoQMoJLgf+HvL2YFsVPTc4+n8w4lNBMAghPr/BVGlTK9SJQ==", + "dependencies": { + "@module-federation/sdk": "0.4.0", + "find-pkg": "2.0.0", + "fs-extra": "9.1.0" + } + }, + "node_modules/@module-federation/manifest": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/manifest/-/manifest-0.4.0.tgz", + "integrity": "sha512-TT2H5z3tFtkVarWz6Zsyi4T6wGxxRQjk3O6lSjYrZzWDYyVnrVhAkBgcN9WlkxqSY/V/ifZlTUWUpfSMObRTig==", + "dependencies": { + "@module-federation/dts-plugin": "0.4.0", + "@module-federation/managers": "0.4.0", + "@module-federation/sdk": "0.4.0", + "chalk": "3.0.0", + "find-pkg": "2.0.0" + } + }, + "node_modules/@module-federation/manifest/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@module-federation/rspack": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/rspack/-/rspack-0.4.0.tgz", + "integrity": "sha512-yPzJwVs/JQcWPw5wy79nEydYAX74TMOJqf4AQijEpdAcE52wWSAm84GymZmAAo55rC74wLGy/DgqQlYNF8WIxw==", + "dependencies": { + "@module-federation/bridge-react-webpack-plugin": "0.4.0", + "@module-federation/dts-plugin": "0.4.0", + "@module-federation/managers": "0.4.0", + "@module-federation/manifest": "0.4.0", + "@module-federation/runtime-tools": "0.4.0", + "@module-federation/sdk": "0.4.0" + }, + "peerDependencies": { + "typescript": "^4.9.0 || ^5.0.0", + "vue-tsc": ">=1.0.24" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, "node_modules/@module-federation/runtime": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.2.8.tgz", @@ -2899,11 +3073,60 @@ "@module-federation/sdk": "0.2.8" } }, - "node_modules/@module-federation/sdk": { + "node_modules/@module-federation/runtime-tools": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime-tools/-/runtime-tools-0.4.0.tgz", + "integrity": "sha512-Mr/ewsZbKmv4ZG3FYNrfdgRh5OauJy5IiZ/Z5jtYlNkLfYjUBHSBkh986l/Bpa385+RCCPs3Lg6yFBQlOiYlug==", + "dependencies": { + "@module-federation/runtime": "0.4.0", + "@module-federation/webpack-bundler-runtime": "0.4.0" + } + }, + "node_modules/@module-federation/runtime-tools/node_modules/@module-federation/runtime": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.4.0.tgz", + "integrity": "sha512-zEhMil0JbB0eS1bflV9qOJFJNmDCWMJbKUN7Xw5OMLDnveKTg9l/rLqDGwt/kAP0/Lhq1PNuXEvYm1CeIKKU8A==", + "dependencies": { + "@module-federation/sdk": "0.4.0" + } + }, + "node_modules/@module-federation/runtime/node_modules/@module-federation/sdk": { "version": "0.2.8", "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.2.8.tgz", "integrity": "sha512-eGMnJxdRDgt6dtMv8gkAlzEbTPWVHb3AHUNUG0w56wcbIF0RHC6kmvpHpSQyq4DVGWv3U4g/ZiH5BvBlqEelDQ==" }, + "node_modules/@module-federation/sdk": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/sdk/-/sdk-0.4.0.tgz", + "integrity": "sha512-fQ/5aABzksXajRLJsocN9XkW8SOB5JRt7xoDmkFFEeb0HLmcbq5K5GURImPm/jysy0Y7a6DkpxhzP9QJU2Z1Zw==" + }, + "node_modules/@module-federation/third-party-dts-extractor": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/third-party-dts-extractor/-/third-party-dts-extractor-0.4.0.tgz", + "integrity": "sha512-zXIfgHiOyZFF6rLjHFt0LJ1SgjO5t/UP05HQgIyMvyZ/Q1cQAcNHGy4iLshrPHthh25sJkClAcrfNTw3/JD9xQ==", + "dependencies": { + "find-pkg": "2.0.0", + "fs-extra": "9.1.0", + "resolve": "1.22.8" + } + }, + "node_modules/@module-federation/webpack-bundler-runtime": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/webpack-bundler-runtime/-/webpack-bundler-runtime-0.4.0.tgz", + "integrity": "sha512-IVwqAuBxvgj2j2HEkV2Q1tDrD9ha6mS0qkBgbtabBjbg8fDc4k+NCmZMB7ou5DCojchqtKXBH2iYNzX062CQ8A==", + "dependencies": { + "@module-federation/runtime": "0.4.0", + "@module-federation/sdk": "0.4.0" + } + }, + "node_modules/@module-federation/webpack-bundler-runtime/node_modules/@module-federation/runtime": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@module-federation/runtime/-/runtime-0.4.0.tgz", + "integrity": "sha512-zEhMil0JbB0eS1bflV9qOJFJNmDCWMJbKUN7Xw5OMLDnveKTg9l/rLqDGwt/kAP0/Lhq1PNuXEvYm1CeIKKU8A==", + "dependencies": { + "@module-federation/sdk": "0.4.0" + } + }, "node_modules/@newrelic/publish-sourcemap": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@newrelic/publish-sourcemap/-/publish-sourcemap-5.1.0.tgz", @@ -2995,15 +3218,6 @@ "react-intl": "^5.25.1 || ^6.4.0" } }, - "node_modules/@openedx/paragon/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@openedx/paragon/node_modules/glob": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", @@ -3132,9 +3346,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.18.0.tgz", - "integrity": "sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.19.1.tgz", + "integrity": "sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==", "peer": true, "engines": { "node": ">=14.0.0" @@ -3201,29 +3415,13 @@ "node": ">=12" } }, - "node_modules/@testing-library/dom/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/@testing-library/dom/node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true - }, "node_modules/@testing-library/jest-dom": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.4.8.tgz", - "integrity": "sha512-JD0G+Zc38f5MBHA4NgxQMR5XtO5Jx9g86jqturNTt2WUfRmLDIY7iKkWHDCCTiDuFMre6nxAD5wHw9W5kI4rGw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", + "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", "dev": true, "dependencies": { "@adobe/css-tools": "^4.4.0", - "@babel/runtime": "^7.9.2", "aria-query": "^5.0.0", "chalk": "^3.0.0", "css.escape": "^1.5.1", @@ -3250,6 +3448,12 @@ "node": ">=8" } }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true + }, "node_modules/@testing-library/react": { "version": "12.1.5", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-12.1.5.tgz", @@ -3314,6 +3518,12 @@ "node": ">=10.13.0" } }, + "node_modules/@tsconfig/node18": { + "version": "18.2.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node18/-/node18-18.2.4.tgz", + "integrity": "sha512-5xxU8vVs9/FNcvm3gE07fPbn9tl6tqGGWA9tSlwsUEkBxtRnTsNmwrV8gasZ9F/EobaSv9+nu8AxUKccw77JpQ==", + "dev": true + }, "node_modules/@types/aria-query": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", @@ -3382,6 +3592,15 @@ "@types/node": "*" } }, + "node_modules/@types/compression": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.7.5.tgz", + "integrity": "sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3405,23 +3624,14 @@ "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==" }, "node_modules/@types/eslint": { - "version": "8.56.11", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.11.tgz", - "integrity": "sha512-sVBpJMf7UPo/wGecYOpk2aQya2VUGeHhe38WG7/mN5FufNSubf5VT9Uh9Uyp8/eLJpu1/tuhJ/qTo4mhSB4V4Q==", + "version": "8.56.12", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.12.tgz", + "integrity": "sha512-03ruubjWyOHlmljCVoxSuNDdmfZDzsrrz0P2LeJsOXr+ZwFQ+0yQIwNCwt/GYhV7Z31fgtXJTAEs+FYlEL851g==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" } }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", @@ -3466,6 +3676,14 @@ "@types/node": "*" } }, + "node_modules/@types/gradient-string": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/gradient-string/-/gradient-string-1.1.6.tgz", + "integrity": "sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==", + "dependencies": { + "@types/tinycolor2": "*" + } + }, "node_modules/@types/hoist-non-react-statics": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz", @@ -3486,9 +3704,9 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==" }, "node_modules/@types/http-proxy": { - "version": "1.17.14", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", - "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", + "version": "1.17.15", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.15.tgz", + "integrity": "sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==", "dependencies": { "@types/node": "*" } @@ -3593,10 +3811,34 @@ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", "dev": true }, + "node_modules/@types/lodash": { + "version": "4.17.7", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.7.tgz", + "integrity": "sha512-8wTvZawATi/lsmNu10/j2hk1KEP0IvjubqPE3cu1Xz7xfXXt5oCq3SNUz4fMIP4XGF9Ky+Ue2tBA3hcS7LSBlA==", + "dev": true + }, + "node_modules/@types/lodash.camelcase": { + "version": "4.3.9", + "resolved": "https://registry.npmjs.org/@types/lodash.camelcase/-/lodash.camelcase-4.3.9.tgz", + "integrity": "sha512-ys9/hGBfsKxzmFI8hckII40V0ASQ83UM2pxfQRghHAwekhH4/jWtjz/3/9YDy7ZpUd/H0k2STSqmPR28dnj7Zg==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, + "node_modules/@types/lodash.merge": { + "version": "4.6.9", + "resolved": "https://registry.npmjs.org/@types/lodash.merge/-/lodash.merge-4.6.9.tgz", + "integrity": "sha512-23sHDPmzd59kUgWyKGiOMO2Qb9YtqRO/x4IhkgNUiPQ1+5MUVqi6bCZeq9nBJ17msjIMbEIO5u+XW4Kz6aGUhQ==", + "dev": true, + "dependencies": { + "@types/lodash": "*" + } + }, "node_modules/@types/markdown-it": { - "version": "14.1.1", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.1.tgz", - "integrity": "sha512-4NpsnpYl2Gt1ljyBGrKMxFYAYvpqbnnkgP/i/g+NLpjEUa3obn1XJCur9YbEXKDAkaXqsR1LbDnGEJ0MmKFxfg==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, "dependencies": { "@types/linkify-it": "^5", @@ -3620,9 +3862,12 @@ "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" }, "node_modules/@types/node": { - "version": "17.0.45", - "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", - "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==" + "version": "18.19.46", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.46.tgz", + "integrity": "sha512-vnRgMS7W6cKa1/0G3/DTtQYpVrZ8c0Xm6UkLaVFrb9jtcVC3okokW09Ki1Qdrj9ISokszD69nY4WDLRlvHlhAA==", + "dependencies": { + "undici-types": "~5.26.4" + } }, "node_modules/@types/node-forge": { "version": "1.3.11", @@ -3677,9 +3922,9 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", - "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", + "version": "4.4.11", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.11.tgz", + "integrity": "sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==", "peer": true, "dependencies": { "@types/react": "*" @@ -3740,6 +3985,11 @@ "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==" }, + "node_modules/@types/tinycolor2": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", + "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==" + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -3758,17 +4008,17 @@ "peer": true }, "node_modules/@types/ws": { - "version": "8.5.11", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.11.tgz", - "integrity": "sha512-4+q7P5h3SpJxaBft0Dzpbr6lmMaqh0Jr2tbhJZ/luAwvD7ohSCniYkwz/pLxuT2h0EOa6QADgJj1Ko+TzRfZ+w==", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dependencies": { "@types/node": "*" } }, "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dependencies": { "@types/yargs-parser": "*" } @@ -3923,34 +4173,12 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" + "url": "https://opencollective.com/typescript-eslint" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { @@ -4282,6 +4510,14 @@ "node": ">=8.9" } }, + "node_modules/adm-zip": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.15.tgz", + "integrity": "sha512-jYPWSeOA8EFoZnucrKCNihqBjoEGQSU4HKgHYQgKNEQ0pQF9a/DYuo/+fAxY76k4qe75LUlLWpAM1QWcBMTOKw==", + "engines": { + "node": ">=12.0" + } + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -4352,6 +4588,14 @@ "ajv": "^6.9.1" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -4443,11 +4687,11 @@ } }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", + "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", "dependencies": { - "dequal": "^2.0.3" + "deep-equal": "^2.0.5" } }, "node_modules/array-buffer-byte-length": { @@ -4659,9 +4903,9 @@ } }, "node_modules/axe-core": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz", - "integrity": "sha512-QbUdXJVTpvUTHU7871ppZkdOLBeGUKBQWHkHrvN2V9IQWGMt61zf3B45BtzjxEJzYuj0JBjBZP/hmYS/R9pmAw==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.0.tgz", + "integrity": "sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==", "engines": { "node": ">=4" } @@ -4796,12 +5040,12 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.4.tgz", - "integrity": "sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==", + "version": "0.10.6", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.10.6.tgz", + "integrity": "sha512-b37+KR2i/khY5sKmWNVQAnitvquQbNdWy6lJdsr0kmquCKEEUgMKK4SboVM3HtfnZilfjr4MMQ7vY58FVWDtIA==", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.1", - "core-js-compat": "^3.36.1" + "@babel/helper-define-polyfill-provider": "^0.6.2", + "core-js-compat": "^3.38.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -4819,22 +5063,25 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" @@ -4893,9 +5140,9 @@ } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.2.0.tgz", + "integrity": "sha512-+o9MG5bPRRBlkVSpfFlMag3n7wMaIZb4YZasU2+/96f+3HTQ4F9DKQeu3K/Sjz1W0umu6xvVq1ON0ipWdMlr3A==", "optional": true, "dependencies": { "streamx": "^2.18.0" @@ -4983,6 +5230,14 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/body-parser/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/body-parser/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5031,12 +5286,11 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -5051,9 +5305,9 @@ } }, "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "funding": [ { "type": "opencollective", @@ -5069,9 +5323,9 @@ } ], "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", "update-browserslist-db": "^1.1.0" }, "bin": { @@ -5089,6 +5343,17 @@ "node-int64": "^0.4.0" } }, + "node_modules/btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==", + "bin": { + "btoa": "bin/btoa.js" + }, + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -5118,13 +5383,25 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", "engines": { "node": ">= 0.8" } }, + "node_modules/cache-content-type": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", + "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", + "dependencies": { + "mime-types": "^2.1.18", + "ylru": "^1.2.0" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/cache-parser": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/cache-parser/-/cache-parser-1.2.5.tgz", @@ -5185,9 +5462,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001643", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001643.tgz", - "integrity": "sha512-ERgWGNleEilSrHM6iUz/zJNSQTP8Mr21wDWpdgvRwcTXGAq6jMtOUPP4dqFPTdKqZ2wKTdtB+uucZ3MRpAUSmg==", + "version": "1.0.30001653", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001653.tgz", + "integrity": "sha512-XGWQVB8wFQ2+9NZwZ10GxTYC5hk0Fa+q8cSkr0tgvMhYhMHP/QC+WTgrePMDBWiWc/pV+1ik82Al20XOK25Gcw==", "funding": [ { "type": "opencollective", @@ -5327,9 +5604,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.0.tgz", + "integrity": "sha512-N1NGmowPlGBLsOZLPvm48StN04V4YvQRL0i6b7ctrVY3epjP/ct7hFLOItz6pDIvRjwpfPxi52a2UWV2ziir8g==" }, "node_modules/classnames": { "version": "2.5.1", @@ -5566,14 +5843,6 @@ "node": ">= 0.8.0" } }, - "node_modules/compression/node_modules/bytes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", - "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", - "engines": { - "node": ">= 0.8" - } - }, "node_modules/compression/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -5587,11 +5856,6 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, - "node_modules/compression/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -5621,6 +5885,25 @@ "node": ">= 0.6" } }, + "node_modules/content-disposition/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", @@ -5652,12 +5935,24 @@ "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==" }, + "node_modules/cookies": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", + "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", + "dependencies": { + "depd": "~2.0.0", + "keygrip": "~1.1.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/core-js-compat": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz", - "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.38.1.tgz", + "integrity": "sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==", "dependencies": { - "browserslist": "^4.23.0" + "browserslist": "^4.23.3" }, "funding": { "type": "opencollective", @@ -5665,9 +5960,9 @@ } }, "node_modules/core-js-pure": { - "version": "3.37.1", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.37.1.tgz", - "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==", + "version": "3.38.1", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.38.1.tgz", + "integrity": "sha512-BY8Etc1FZqdw1glX0XNOq2FDwfrg/VGqoZOZCdaL+UmdaqDwQwYXkMJT4t6In+zfEfOJDcM9T0KdbBeJg8KKCQ==", "hasInstallScript": true, "funding": { "type": "opencollective", @@ -5740,6 +6035,17 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -6074,15 +6380,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-format": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz", + "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/debounce": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.2.1.tgz", "integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==" }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dependencies": { "ms": "2.1.2" }, @@ -6140,7 +6454,6 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, "dependencies": { "array-buffer-byte-length": "^1.0.0", "call-bind": "^1.0.5", @@ -6311,6 +6624,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -6323,6 +6641,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "peer": true, "engines": { "node": ">=6" } @@ -6439,9 +6758,9 @@ } }, "node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true }, "node_modules/dom-converter": { @@ -6542,36 +6861,6 @@ "tslib": "^2.0.3" } }, - "node_modules/dotenv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", - "engines": { - "node": ">=10" - } - }, - "node_modules/dotenv-defaults": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dotenv-defaults/-/dotenv-defaults-2.0.2.tgz", - "integrity": "sha512-iOIzovWfsUHU91L5i8bJce3NYK5JXeAwH50Jh6+ARUdLiiGlYWfGw6UkzsYqaXZH/hjE/eCd/PlfM/qqyK0AMg==", - "dependencies": { - "dotenv": "^8.2.0" - } - }, - "node_modules/dotenv-webpack": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/dotenv-webpack/-/dotenv-webpack-8.0.1.tgz", - "integrity": "sha512-CdrgfhZOnx4uB18SgaoP9XHRN2v48BbjuXQsZY5ixs5A8579NxQkmMxRtI7aTwSiSQcM2ao12Fdu+L3ZS3bG4w==", - "dependencies": { - "dotenv-defaults": "^2.0.2" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "webpack": "^4 || ^5" - } - }, "node_modules/duplexer": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", @@ -6583,9 +6872,9 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" }, "node_modules/electron-to-chromium": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.0.tgz", - "integrity": "sha512-Vb3xHHYnLseK8vlMJQKJYXJ++t4u1/qJ3vykuVrVjvdiOEhYyT1AuP4x03G8EnPmYvYOhe9T+dADTmthjRQMkA==" + "version": "1.5.13", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz", + "integrity": "sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==" }, "node_modules/email-prop-type": { "version": "3.0.1", @@ -6617,9 +6906,9 @@ } }, "node_modules/emoji-regex": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", - "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -6777,7 +7066,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "get-intrinsic": "^1.1.3", @@ -7025,9 +7313,9 @@ } }, "node_modules/eslint-module-utils": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.1.tgz", - "integrity": "sha512-rXDXR3h7cs7dy9RNpUlQf80nX31XWJEyGq1tRMo+6GsO5VmTe4UTwtmonAD4ZkAsrfMVDA2wlGJ3790Ys+D49Q==", + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.2.tgz", + "integrity": "sha512-3XnC5fDyc8M4J2E8pt8pmSVRX2M+5yWMCfI/kDZwauQeFgzQOuhcRBFKjTeJagqgk4sFKxe1mvNVnaWwImx/Tg==", "dependencies": { "debug": "^3.2.7" }, @@ -7102,6 +7390,15 @@ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" } }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-import/node_modules/debug": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", @@ -7121,6 +7418,17 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-jsx-a11y": { "version": "6.7.1", "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.7.1.tgz", @@ -7150,11 +7458,31 @@ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" } }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-jsx-a11y/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==" }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react": { "version": "7.32.2", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz", @@ -7194,6 +7522,15 @@ "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" } }, + "node_modules/eslint-plugin-react/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/eslint-plugin-react/node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -7205,6 +7542,17 @@ "node": ">=0.10.0" } }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint-plugin-react/node_modules/resolve": { "version": "2.0.0-next.5", "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", @@ -7222,23 +7570,18 @@ } }, "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dependencies": { "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "estraverse": "^5.2.0" }, "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-scope/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "engines": { - "node": ">=4.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { @@ -7257,19 +7600,13 @@ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, "node_modules/eslint/node_modules/find-up": { @@ -7326,6 +7663,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/eslint/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -7473,7 +7821,18 @@ "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", "engines": { - "node": ">=6" + "node": ">=6" + } + }, + "node_modules/expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "dependencies": { + "homedir-polyfill": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/expect": { @@ -7545,6 +7904,25 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/express/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -7796,6 +8174,28 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" }, + "node_modules/find-file-up": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/find-file-up/-/find-file-up-2.0.1.tgz", + "integrity": "sha512-qVdaUhYO39zmh28/JLQM5CoYN9byEOKEH4qfa8K1eNV17W0UUMJ9WgbR/hHFH+t5rcl+6RTb5UC7ck/I+uRkpQ==", + "dependencies": { + "resolve-dir": "^1.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-pkg/-/find-pkg-2.0.0.tgz", + "integrity": "sha512-WgZ+nKbELDa6N3i/9nrHeNznm+lY3z4YfhDDWgW+5P0pdmMj26bxaxU11ookgY3NyP9GC7HvZ9etp0jRFqGEeQ==", + "dependencies": { + "find-file-up": "^2.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -7935,6 +8335,15 @@ } } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/cosmiconfig": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz", @@ -7950,6 +8359,17 @@ "node": ">=8" } }, + "node_modules/fork-ts-checker-webpack-plugin/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/schema-utils": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", @@ -8235,6 +8655,26 @@ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -8328,6 +8768,18 @@ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, + "node_modules/gradient-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gradient-string/-/gradient-string-2.0.2.tgz", + "integrity": "sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==", + "dependencies": { + "chalk": "^4.1.2", + "tinygradient": "^1.1.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -8473,6 +8925,17 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dependencies": { + "parse-passwd": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -8503,11 +8966,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/hpack.js/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -8624,6 +9082,54 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/http-assert": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", + "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", + "dependencies": { + "deep-equal": "~1.0.1", + "http-errors": "~1.8.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-assert/node_modules/deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==" + }, + "node_modules/http-assert/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/http-assert/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -8787,9 +9293,9 @@ ] }, "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "engines": { "node": ">= 4" } @@ -9038,7 +9544,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -9142,9 +9647,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.0.tgz", - "integrity": "sha512-Dd+Lb2/zvk9SKy1TGCt1wFJFo/MWBPMX5x7KcvLajWTGuomczdQX61PvY5yK6SVACwpoexWo81IfFyoKY2QnTA==", + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", "dependencies": { "hasown": "^2.0.2" }, @@ -9221,6 +9726,20 @@ "node": ">=6" } }, + "node_modules/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -9245,7 +9764,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9378,7 +9896,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9469,7 +9986,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", - "dev": true, "engines": { "node": ">= 0.4" }, @@ -9492,7 +10008,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.3.tgz", "integrity": "sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "get-intrinsic": "^1.2.4" @@ -9504,6 +10019,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -9533,6 +10056,14 @@ "node": ">=0.10.0" } }, + "node_modules/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -10706,6 +11237,17 @@ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-3.1.2.tgz", "integrity": "sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==" }, + "node_modules/keygrip": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", + "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "dependencies": { + "tsscmp": "1.0.6" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -10739,6 +11281,87 @@ "node": ">=6" } }, + "node_modules/koa": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.3.tgz", + "integrity": "sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg==", + "dependencies": { + "accepts": "^1.3.5", + "cache-content-type": "^1.0.0", + "content-disposition": "~0.5.2", + "content-type": "^1.0.4", + "cookies": "~0.9.0", + "debug": "^4.3.2", + "delegates": "^1.0.0", + "depd": "^2.0.0", + "destroy": "^1.0.4", + "encodeurl": "^1.0.2", + "escape-html": "^1.0.3", + "fresh": "~0.5.2", + "http-assert": "^1.3.0", + "http-errors": "^1.6.3", + "is-generator-function": "^1.0.7", + "koa-compose": "^4.1.0", + "koa-convert": "^2.0.0", + "on-finished": "^2.3.0", + "only": "~0.0.2", + "parseurl": "^1.3.2", + "statuses": "^1.5.0", + "type-is": "^1.6.16", + "vary": "^1.1.2" + }, + "engines": { + "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + } + }, + "node_modules/koa-compose": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", + "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==" + }, + "node_modules/koa-convert": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", + "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", + "dependencies": { + "co": "^4.6.0", + "koa-compose": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/koa/node_modules/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", + "dependencies": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/http-errors/node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/koa/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/language-subtag-registry": { "version": "0.3.23", "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", @@ -10753,9 +11376,9 @@ } }, "node_modules/launch-editor": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.0.tgz", - "integrity": "sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==", + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.8.1.tgz", + "integrity": "sha512-elBx2l/tp9z99X5H/qev8uyDywVh0VXAwEbjk8kJhnc5grOFkGh7aW6q55me9xnYbss261XtnUrysZ+XvGbhQA==", "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" @@ -10872,6 +11495,11 @@ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==" }, + "node_modules/lodash.clonedeepwith": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeepwith/-/lodash.clonedeepwith-4.5.0.tgz", + "integrity": "sha512-QRBRSxhbtsX1nc0baxSkkK5WlVTTm/s48DSukcGcWZwIyI8Zz+lB+kFiELJXtzfH4Aj6kMWQ1VWW4U5uUDgZMA==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -10924,6 +11552,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/log4js": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz", + "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "flatted": "^3.2.7", + "rfdc": "^1.3.0", + "streamroller": "^3.1.5" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/long-timeout": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/long-timeout/-/long-timeout-0.1.1.tgz", + "integrity": "sha512-BFRuQUqc7x2NWxfJBCyUrN8iYUYznzL9JROmRz1gZ6KlOIgmoD+njPVbb+VNn2nGMKggMsK79iUNErillsrx7w==" + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -10951,6 +11599,14 @@ "yallist": "^3.0.2" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -10961,11 +11617,11 @@ } }, "node_modules/magic-string": { - "version": "0.30.10", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", - "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15" + "@jridgewell/sourcemap-codec": "^1.5.0" } }, "node_modules/mailto-link": { @@ -11126,9 +11782,9 @@ } }, "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -11149,9 +11805,9 @@ } }, "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "version": "1.53.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz", + "integrity": "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==", "engines": { "node": ">= 0.6" } @@ -11167,6 +11823,14 @@ "node": ">= 0.6" } }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", @@ -11238,14 +11902,17 @@ "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -11354,9 +12021,9 @@ } }, "node_modules/node-abi": { - "version": "3.65.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.65.0.tgz", - "integrity": "sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==", + "version": "3.67.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.67.0.tgz", + "integrity": "sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==", "dependencies": { "semver": "^7.3.5" }, @@ -11398,6 +12065,19 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" }, + "node_modules/node-schedule": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/node-schedule/-/node-schedule-2.1.1.tgz", + "integrity": "sha512-OXdegQq03OmXEjt2hZP33W2YPs/E5BcFQks46+G2gAxs4gHOIVD1u7EqlYLYSKsaIpyKCK9Gbk0ta1/gjRSMRQ==", + "dependencies": { + "cron-parser": "^4.2.0", + "long-timeout": "0.1.1", + "sorted-array-functions": "^1.3.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/nodemon": { "version": "3.1.4", "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", @@ -11426,13 +12106,35 @@ "url": "https://opencollective.com/nodemon" } }, + "node_modules/nodemon/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/nodemon/node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "engines": { - "node": ">=4" + "node": ">=4" + } + }, + "node_modules/nodemon/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, "node_modules/nodemon/node_modules/semver": { @@ -11536,7 +12238,6 @@ "version": "1.1.6", "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, "dependencies": { "call-bind": "^1.0.7", "define-properties": "^1.2.1" @@ -11681,6 +12382,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/only": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", + "integrity": "sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==" + }, "node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -11857,6 +12563,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/parse5": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", @@ -12098,7 +12812,6 @@ "url": "https://github.com/sponsors/ai" } ], - "license": "MIT", "dependencies": { "nanoid": "^3.3.7", "picocolors": "^1.0.1", @@ -12588,9 +13301,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.1.tgz", - "integrity": "sha512-b4dlw/9V8A71rLIDsSwVmak9z2DuBUB7CA1/wSdelNEzqsjoSPeADTWNO09lpH49Diy3/JIZ2bSPB1dI3LJCHg==", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" @@ -12852,66 +13565,6 @@ } ] }, - "node_modules/purgecss": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/purgecss/-/purgecss-5.0.0.tgz", - "integrity": "sha512-RAnuxrGuVyLLTr8uMbKaxDRGWMgK5CCYDfRyUNNcaz5P3kGgD2b7ymQGYEyo2ST7Tl/ScwFgf5l3slKMxHSbrw==", - "dependencies": { - "commander": "^9.0.0", - "glob": "^8.0.3", - "postcss": "^8.4.4", - "postcss-selector-parser": "^6.0.7" - }, - "bin": { - "purgecss": "bin/purgecss.js" - } - }, - "node_modules/purgecss/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/purgecss/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "engines": { - "node": "^12.20.0 || >=14" - } - }, - "node_modules/purgecss/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/purgecss/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -12973,6 +13626,11 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, + "node_modules/rambda": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/rambda/-/rambda-9.3.0.tgz", + "integrity": "sha512-cl/7DCCKNxmsbc0dXZTJTY08rvDdzLhVfE6kPBson1fWzDapLzv0RKSzjpmAqP53fkQqAvq05gpUVHTrUNsuxg==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -13003,6 +13661,14 @@ "node": ">= 0.8" } }, + "node_modules/raw-body/node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", @@ -13233,9 +13899,9 @@ "peer": true }, "node_modules/react-focus-lock": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.12.1.tgz", - "integrity": "sha512-lfp8Dve4yJagkHiFrC1bGtib3mF2ktqwPJw4/WGcgPW+pJ/AVQA5X2vI7xgp13FcxFEpYBBHpXai/N2DBNC0Jw==", + "version": "2.13.2", + "resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.13.2.tgz", + "integrity": "sha512-T/7bsofxYqnod2xadvuwjGKHOoL5GH7/EIPI5UyEvaU/c2CcphvGI371opFtuY/SYdbMsNiuF4HsHQ50nA/TKQ==", "peer": true, "dependencies": { "@babel/runtime": "^7.0.0", @@ -13520,12 +14186,12 @@ } }, "node_modules/react-router": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.25.1.tgz", - "integrity": "sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.26.1.tgz", + "integrity": "sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==", "peer": true, "dependencies": { - "@remix-run/router": "1.18.0" + "@remix-run/router": "1.19.1" }, "engines": { "node": ">=14.0.0" @@ -13535,13 +14201,13 @@ } }, "node_modules/react-router-dom": { - "version": "6.25.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.25.1.tgz", - "integrity": "sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==", + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.26.1.tgz", + "integrity": "sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==", "peer": true, "dependencies": { - "@remix-run/router": "1.18.0", - "react-router": "6.25.1" + "@remix-run/router": "1.19.1", + "react-router": "6.26.1" }, "engines": { "node": ">=14.0.0" @@ -13677,6 +14343,26 @@ "node": ">=6.0.0" } }, + "node_modules/recursive-readdir/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/recursive-readdir/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -13862,6 +14548,57 @@ "node": ">=8" } }, + "node_modules/resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "dependencies": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", + "dependencies": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "dependencies": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-dir/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -13941,6 +14678,11 @@ "node": ">=0.10.0" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==" + }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -14028,23 +14770,9 @@ } }, "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, "node_modules/safe-regex-test": { "version": "1.0.3", @@ -14603,6 +15331,11 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/sorted-array-functions": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sorted-array-functions/-/sorted-array-functions-1.3.0.tgz", + "integrity": "sha512-2sqgzeFlid6N4Z2fUQ1cvFmTOLRi/sEDzSQ0OKYchqgoPmQBVyM3959qYx3fpS6Esef80KjmpgPeEr028dP3OA==" + }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -14749,7 +15482,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.0.0.tgz", "integrity": "sha512-iCGQj+0l0HOdZ2AEeBADlsRC+vsnDsZsbdSiH1yNSjcfKM7fdpCMfqAL/dwF5BLiw/XhRft/Wax6zQbhq2BcjQ==", - "dev": true, "dependencies": { "internal-slot": "^1.0.4" }, @@ -14757,10 +15489,52 @@ "node": ">= 0.4" } }, + "node_modules/streamroller": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz", + "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==", + "dependencies": { + "date-format": "^4.0.14", + "debug": "^4.3.4", + "fs-extra": "^8.1.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/streamroller/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/streamroller/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/streamroller/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.19.0.tgz", + "integrity": "sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg==", "dependencies": { "fast-fifo": "^1.3.2", "queue-tick": "^1.0.1", @@ -14787,6 +15561,25 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -15029,11 +15822,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/superagent/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, "node_modules/superagent/node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -15194,9 +15982,9 @@ } }, "node_modules/terser": { - "version": "5.31.3", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.3.tgz", - "integrity": "sha512-pAfYn3NIZLyZpa83ZKigvj6Rn9c/vd5KfYGX7cN1mnzqgDcxWvrU5ZtAfIKhEXz9nRecw4z3LXkjaq96/qZqAA==", + "version": "5.31.6", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.6.tgz", + "integrity": "sha512-PQ4DAriWzKj+qgehQ7LK5bQqCFNMmlhjR2PFFLuqGCpuCAauxemVBWwWOxo3UIwWQx8+Pr61Df++r76wDmkQBg==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -15322,6 +16110,26 @@ "node": ">=8" } }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/text-decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.1.tgz", @@ -15356,6 +16164,20 @@ "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==" + }, + "node_modules/tinygradient": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/tinygradient/-/tinygradient-1.1.5.tgz", + "integrity": "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==", + "dependencies": { + "@types/tinycolor2": "^1.4.0", + "tinycolor2": "^1.0.0" + } + }, "node_modules/tmp": { "version": "0.0.33", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", @@ -15522,9 +16344,17 @@ } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==" + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==" + }, + "node_modules/tsscmp": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", + "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", + "engines": { + "node": ">=0.6.x" + } }, "node_modules/tunnel-agent": { "version": "0.6.0", @@ -15702,11 +16532,16 @@ "dev": true }, "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==", + "version": "1.13.7", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", + "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", "dev": true }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -15723,6 +16558,11 @@ "emoji-regex": "10.3.0" } }, + "node_modules/unicode-emoji-utils/node_modules/emoji-regex": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", + "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==" + }, "node_modules/unicode-match-property-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", @@ -15784,6 +16624,15 @@ "node": ">= 0.8" } }, + "node_modules/upath": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz", + "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", @@ -16011,9 +16860,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", - "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", + "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -16048,11 +16897,10 @@ } }, "node_modules/webpack": { - "version": "5.93.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", - "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", + "version": "5.94.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.94.0.tgz", + "integrity": "sha512-KcsGn50VT+06JH/iunZJedYGUJS5FGjow8wb9c0v5n1Om8O1g4L6LjtfxwlXIATopoQu+vOXXa7gYisWxCoPyg==", "dependencies": { - "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.5", "@webassemblyjs/ast": "^1.12.1", "@webassemblyjs/wasm-edit": "^1.12.1", @@ -16061,7 +16909,7 @@ "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.0", + "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -16331,6 +17179,26 @@ "node": ">=0.10.0" } }, + "node_modules/webpack/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/webpack/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "engines": { + "node": ">=4.0" + } + }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -16452,7 +17320,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", - "dev": true, "dependencies": { "is-map": "^2.0.3", "is-set": "^2.0.3", @@ -16529,9 +17396,9 @@ } }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, @@ -16613,6 +17480,14 @@ "node": ">=10" } }, + "node_modules/ylru": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.4.0.tgz", + "integrity": "sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index 6f89797a..3e0c08e2 100644 --- a/package.json +++ b/package.json @@ -7,27 +7,31 @@ }, "main": "dist/index.js", "module": "dist/index.js", - "types": "dist/frontend-base.d.ts", + "types": "dist/index.d.ts", "files": [ "/dist", "/config", - "/cli", "/shell/**/*.scss" ], "bin": { - "openedx": "dist/bin/openedx.js", - "intl-imports.js": "dist/cli/scripts/intl-imports.js", - "transifex-utils.js": "dist/cli/scripts/transifex-utils.js" + "intl-imports.js": "dist/tools/cli/scripts/intl-imports.js", + "openedx": "dist/tools/cli/openedx.js", + "transifex-utils.js": "dist/tools/cli/scripts/transifex-utils.js" }, "scripts": { "build": "make build", + "temp:pack-it": "npm run temp:prep-pack && npm run temp:pack-base-test && npm run temp:pack-project && npm run temp:pack-project-module", + "temp:prep-pack": "npm run build && npm pack", + "temp:pack-base-test": "cd ../frontend-app-base-test && npm i --save-peer ../frontend-base/openedx-frontend-base-1.0.0.tgz && cd ../frontend-base", + "temp:pack-project": "cd ../frontend-project-test && npm i ../frontend-base/openedx-frontend-base-1.0.0.tgz && cd ../frontend-base", + "temp:pack-project-module": "cd ../frontend-project-module-test && npm i ../frontend-base/openedx-frontend-base-1.0.0.tgz && cd ../frontend-base", "clean": "rm -rf dist", "docs": "jsdoc -c jsdoc.json", "docs:watch": "nodemon -w runtime -w docs/template -w README.md -e js,jsx,ts,tsx --exec npm run docs", "lint": "eslint .", - "test": "npm run test:cli; npm run test:app; npm run test:runtime; npm run test:shell", - "test:app": "npm --prefix ./test-app i; npm --prefix ./test-app run lint; npm --prefix ./test-app run build", - "test:cli": "jest cli --config cli/jest.config.js --no-cache", + "test": "npm run test:tools && npm run test:app && npm run test:runtime && npm run test:shell", + "test:app": "npm --prefix ./test-app i; npm --prefix ./test-app run build", + "test:tools": "jest tools --config tools/jest.config.js --no-cache", "test:runtime": "jest runtime --config runtime/jest.config.js --no-cache", "test:shell": "jest shell --config shell/jest.config.js --no-cache" }, @@ -54,9 +58,10 @@ "@formatjs/intl-pluralrules": "^4.3.3", "@formatjs/intl-relativetimeformat": "^10.0.1", "@formatjs/ts-transformer": "^3.13.14", - "@fullhuman/postcss-purgecss": "5.0.0", + "@module-federation/enhanced": "^0.4.0", "@module-federation/runtime": "^0.2.6", "@pmmmwh/react-refresh-webpack-plugin": "0.5.15", + "@types/gradient-string": "^1.1.6", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", "autoprefixer": "10.4.19", @@ -67,10 +72,9 @@ "chalk": "4.1.2", "classnames": "^2.5.1", "clean-webpack-plugin": "4.0.0", + "compression": "^1.7.4", "css-loader": "5.2.7", "cssnano": "6.0.3", - "dotenv": "8.6.0", - "dotenv-webpack": "8.0.1", "eslint": "8.44.0", "eslint-config-airbnb": "19.0.4", "eslint-config-airbnb-typescript": "^17.0.0", @@ -83,6 +87,7 @@ "file-loader": "6.2.0", "form-urlencoded": "^6.1.5", "glob": "^7.2.3", + "gradient-string": "^2.0.2", "history": "^4.10.1", "html-webpack-plugin": "5.6.0", "i18n-iso-countries": "^4.3.1", @@ -130,7 +135,12 @@ "@testing-library/jest-dom": "^6.4.6", "@testing-library/react": "^12.1.5", "@testing-library/react-hooks": "^8.0.1", + "@tsconfig/node18": "^18.2.4", + "@types/compression": "^1.7.5", "@types/jest": "^29.5.12", + "@types/lodash.camelcase": "^4.3.9", + "@types/lodash.merge": "^4.6.9", + "@types/node": "^18.19.43", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.11", "axios-mock-adapter": "^1.22.0", @@ -144,7 +154,8 @@ "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^8.1.3", - "react-router-dom": "^6.24.1", + "react-router": "^6.26.1", + "react-router-dom": "^6.26.1", "redux": "^4.2.1" } } diff --git a/runtime/__mocks__/env.config.js b/runtime/__mocks__/env.config.js deleted file mode 100644 index f053ebf7..00000000 --- a/runtime/__mocks__/env.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/runtime/auth/AxiosJwtAuthService.js b/runtime/auth/AxiosJwtAuthService.js index de97b0ef..a6311bf3 100644 --- a/runtime/auth/AxiosJwtAuthService.js +++ b/runtime/auth/AxiosJwtAuthService.js @@ -1,7 +1,7 @@ import axios from 'axios'; import PropTypes from 'prop-types'; import { logFrontendAuthError } from './utils'; -import { camelCaseObject, ensureDefinedConfig } from '../utils'; +import { camelCaseObject } from '../utils'; import createJwtTokenProviderInterceptor from './interceptors/createJwtTokenProviderInterceptor'; import createCsrfTokenProviderInterceptor from './interceptors/createCsrfTokenProviderInterceptor'; import createProcessAxiosRequestErrorInterceptor from './interceptors/createProcessAxiosRequestErrorInterceptor'; @@ -15,7 +15,7 @@ const optionsPropTypes = { LMS_BASE_URL: PropTypes.string.isRequired, LOGIN_URL: PropTypes.string.isRequired, LOGOUT_URL: PropTypes.string.isRequired, - REFRESH_ACCESS_TOKEN_ENDPOINT: PropTypes.string.isRequired, + REFRESH_ACCESS_TOKEN_API_PATH: PropTypes.string.isRequired, ACCESS_TOKEN_COOKIE_NAME: PropTypes.string.isRequired, CSRF_TOKEN_API_PATH: PropTypes.string.isRequired, }).isRequired, @@ -37,7 +37,7 @@ class AxiosJwtAuthService { * @param {string} options.config.LMS_BASE_URL * @param {string} options.config.LOGIN_URL * @param {string} options.config.LOGOUT_URL - * @param {string} options.config.REFRESH_ACCESS_TOKEN_ENDPOINT + * @param {string} options.config.REFRESH_ACCESS_TOKEN_API_PATH * @param {string} options.config.ACCESS_TOKEN_COOKIE_NAME * @param {string} options.config.CSRF_TOKEN_API_PATH * @param {Object} options.loggingService requires logError and logInfo methods @@ -49,7 +49,6 @@ class AxiosJwtAuthService { this.cachedHttpClient = null; this.authenticatedUser = null; - ensureDefinedConfig(options, 'AuthService'); PropTypes.checkPropTypes(optionsPropTypes, options, 'options', 'AuthService'); this.config = options.config; @@ -57,7 +56,8 @@ class AxiosJwtAuthService { this.jwtTokenService = new AxiosJwtTokenService( this.loggingService, this.config.ACCESS_TOKEN_COOKIE_NAME, - this.config.REFRESH_ACCESS_TOKEN_ENDPOINT, + this.config.LMS_BASE_URL, + this.config.REFRESH_ACCESS_TOKEN_API_PATH, ); this.csrfTokenService = new AxiosCsrfTokenService(this.config.CSRF_TOKEN_API_PATH); this.authenticatedHttpClient = this.addAuthenticationToHttpClient(axios.create()); @@ -305,7 +305,7 @@ class AxiosJwtAuthService { * * @param {HttpClient} newHttpClient * @param {Object} config - * @param {string} [config.REFRESH_ACCESS_TOKEN_ENDPOINT] + * @param {string} [config.REFRESH_ACCESS_TOKEN_API_PATH] * @param {string} [config.ACCESS_TOKEN_COOKIE_NAME] * @param {string} [config.CSRF_TOKEN_API_PATH] * @returns {HttpClient} A configured Axios HTTP client. diff --git a/runtime/auth/AxiosJwtAuthService.test.jsx b/runtime/auth/AxiosJwtAuthService.test.jsx index 9dd65c6f..37a351a1 100644 --- a/runtime/auth/AxiosJwtAuthService.test.jsx +++ b/runtime/auth/AxiosJwtAuthService.test.jsx @@ -1,7 +1,8 @@ /* eslint-disable arrow-body-style, no-console */ import axios from 'axios'; -import Cookies from 'universal-cookie'; import MockAdapter from 'axios-mock-adapter'; +import Cookies from 'universal-cookie'; +import { getConfig } from '../config'; import AxiosJwtAuthService from './AxiosJwtAuthService'; const mockLoggingService = { @@ -11,15 +12,7 @@ const mockLoggingService = { }; const authOptions = { - config: { - BASE_URL: process.env.BASE_URL, - ACCESS_TOKEN_COOKIE_NAME: process.env.ACCESS_TOKEN_COOKIE_NAME, - CSRF_TOKEN_API_PATH: '/get-csrf-token', - LMS_BASE_URL: process.env.LMS_BASE_URL, - LOGIN_URL: process.env.LOGIN_URL, - LOGOUT_URL: process.env.LOGOUT_URL, - REFRESH_ACCESS_TOKEN_ENDPOINT: process.env.REFRESH_ACCESS_TOKEN_ENDPOINT, - }, + config: getConfig(), loggingService: mockLoggingService, }; @@ -77,7 +70,7 @@ Object.keys(jwtTokens).forEach((jwtTokenName) => { }); const mockCsrfToken = 'thetokenvalue'; -const mockApiEndpointPath = `${process.env.BASE_URL}/api/v1/test`; +const mockApiEndpointPath = `${authOptions.config.BASE_URL}/api/v1/test`; global.location ??= { ...global.location, assign: jest.fn() }; @@ -112,16 +105,16 @@ const setJwtTokenRefreshResponseTo = (status, jwtCookieValue, responseEpochSecon }); }; -function expectLogout(redirectUrl = process.env.BASE_URL) { +function expectLogout(redirectUrl = authOptions.config.BASE_URL) { const encodedRedirectUrl = encodeURIComponent(redirectUrl); expect(global.location.assign) - .toHaveBeenCalledWith(`${process.env.LOGOUT_URL}?redirect_url=${encodedRedirectUrl}`); + .toHaveBeenCalledWith(`${authOptions.config.LOGOUT_URL}?redirect_url=${encodedRedirectUrl}`); } -function expectLogin(redirectUrl = process.env.BASE_URL) { +function expectLogin(redirectUrl = authOptions.config.BASE_URL) { const encodedRedirectUrl = encodeURIComponent(redirectUrl); expect(global.location.assign) - .toHaveBeenCalledWith(`${process.env.LOGIN_URL}?next=${encodedRedirectUrl}`); + .toHaveBeenCalledWith(`${authOptions.config.LOGIN_URL}?next=${encodedRedirectUrl}`); } // customAttributes is sent into expect.objectContaining @@ -210,9 +203,7 @@ beforeEach(() => { axiosMock.onGet('/unauthorized').reply(401); axiosMock.onGet('/forbidden').reply(403); axiosMock.onAny().reply(200); - csrfTokensAxiosMock - .onGet(process.env.CSRF_TOKEN_REFRESH) - .reply(200, { csrfToken: mockCsrfToken }); + csrfTokensAxiosMock.onGet().reply(200, { csrfToken: mockCsrfToken }); axios.defaults.maxRetries = 0; csrfTokensAxios.defaults.maxRetries = 0; accessTokenAxios.defaults.maxRetries = 0; @@ -739,7 +730,7 @@ describe('redirectToLogin', () => { service.redirectToLogin('http://edx.org/dashboard'); expectLogin('http://edx.org/dashboard'); service.redirectToLogin(); - expectLogin(process.env.BASE_URL); + expectLogin(authOptions.config.BASE_URL); }); }); @@ -748,7 +739,7 @@ describe('redirectToLogout', () => { service.redirectToLogout('http://edx.org/'); expectLogout('http://edx.org/'); service.redirectToLogout(); - expectLogout(process.env.BASE_URL); + expectLogout(authOptions.config.BASE_URL); }); }); @@ -834,15 +825,15 @@ describe('ensureAuthenticatedUser', () => { it('attempts to refresh a missing jwt token and redirects user to login', () => { setJwtCookieTo(null); expect.hasAssertions(); - return service.ensureAuthenticatedUser(`${process.env.BASE_URL}/route`).catch((unauthorizedError) => { + return service.ensureAuthenticatedUser(`${authOptions.config.BASE_URL}/route`).catch((unauthorizedError) => { expect(unauthorizedError.isRedirecting).toBe(true); expectSingleCallToJwtTokenRefresh(); - expectLogin(`${process.env.BASE_URL}/route`); + expectLogin(`${authOptions.config.BASE_URL}/route`); }); }); it('throws an error and does not redirect if the referrer is the login page', () => { - jest.spyOn(global.document, 'referrer', 'get').mockReturnValue(process.env.LOGIN_URL); + jest.spyOn(global.document, 'referrer', 'get').mockReturnValue(authOptions.config.LOGIN_URL); setJwtCookieTo(null); expect.hasAssertions(); return service.ensureAuthenticatedUser().catch(() => { diff --git a/runtime/auth/AxiosJwtTokenService.js b/runtime/auth/AxiosJwtTokenService.js index 514705d1..9b28ed2e 100644 --- a/runtime/auth/AxiosJwtTokenService.js +++ b/runtime/auth/AxiosJwtTokenService.js @@ -9,10 +9,11 @@ export default class AxiosJwtTokenService { return !token || token.exp < Date.now() / 1000; } - constructor(loggingService, tokenCookieName, tokenRefreshEndpoint) { + constructor(loggingService, tokenCookieName, tokenRefreshBaseUrl, tokenRefreshPath) { this.loggingService = loggingService; this.tokenCookieName = tokenCookieName; - this.tokenRefreshEndpoint = tokenRefreshEndpoint; + this.tokenRefreshBaseUrl = tokenRefreshBaseUrl; + this.tokenRefreshPath = tokenRefreshPath; this.httpClient = axios.create(); // Set withCredentials to true. Enables cross-site Access-Control requests @@ -59,7 +60,7 @@ export default class AxiosJwtTokenService { let axiosResponse; try { try { - axiosResponse = await this.httpClient.post(this.tokenRefreshEndpoint); + axiosResponse = await this.httpClient.post(`${this.tokenRefreshBaseUrl}${this.tokenRefreshPath}`); // eslint-disable-next-line max-len if (axiosResponse.data && axiosResponse.data.response_epoch_seconds) { responseServerEpochSeconds = axiosResponse.data.response_epoch_seconds; diff --git a/runtime/auth/MockAuthService.js b/runtime/auth/MockAuthService.js index 9b29dccb..cc9e7c88 100644 --- a/runtime/auth/MockAuthService.js +++ b/runtime/auth/MockAuthService.js @@ -1,6 +1,5 @@ import axios from 'axios'; import PropTypes from 'prop-types'; -import { ensureDefinedConfig } from '../utils'; const userPropTypes = PropTypes.shape({ userId: PropTypes.string.isRequired, @@ -15,7 +14,7 @@ const optionsPropTypes = { LMS_BASE_URL: PropTypes.string.isRequired, LOGIN_URL: PropTypes.string.isRequired, LOGOUT_URL: PropTypes.string.isRequired, - REFRESH_ACCESS_TOKEN_ENDPOINT: PropTypes.string.isRequired, + REFRESH_ACCESS_TOKEN_API_PATH: PropTypes.string.isRequired, ACCESS_TOKEN_COOKIE_NAME: PropTypes.string.isRequired, CSRF_TOKEN_API_PATH: PropTypes.string.isRequired, }).isRequired, @@ -81,28 +80,23 @@ class MockAuthService { * @param {string} options.config.LMS_BASE_URL * @param {string} options.config.LOGIN_URL * @param {string} options.config.LOGOUT_URL - * @param {string} options.config.REFRESH_ACCESS_TOKEN_ENDPOINT + * @param {string} options.config.REFRESH_ACCESS_TOKEN_API_PATH * @param {string} options.config.ACCESS_TOKEN_COOKIE_NAME * @param {string} options.config.CSRF_TOKEN_API_PATH - * @param {Object} options.config.hydratedAuthenticatedUser - * @param {Object} options.config.authenticatedUser * @param {Object} options.loggingService requires logError and logInfo methods */ constructor(options) { this.authenticatedHttpClient = null; this.httpClient = null; - ensureDefinedConfig(options, 'AuthService'); PropTypes.checkPropTypes(optionsPropTypes, options, 'options', 'AuthService'); this.config = options.config; this.loggingService = options.loggingService; // Mock user - this.authenticatedUser = this.config.authenticatedUser ? this.config.authenticatedUser : null; - this.hydratedAuthenticatedUser = this.config.hydratedAuthenticatedUser - ? this.config.hydratedAuthenticatedUser - : {}; + this.authenticatedUser = null; + this.hydratedAuthenticatedUser = {}; this.authenticatedHttpClient = axios.create(); this.httpClient = axios.create(); diff --git a/runtime/auth/interface.js b/runtime/auth/interface.js index efddf576..ecf85231 100644 --- a/runtime/auth/interface.js +++ b/runtime/auth/interface.js @@ -35,7 +35,6 @@ * @module Auth */ import PropTypes from 'prop-types'; -import { publish } from '../pubSub'; /** * @constant @@ -60,7 +59,7 @@ const optionsShape = { LMS_BASE_URL: PropTypes.string.isRequired, LOGIN_URL: PropTypes.string.isRequired, LOGOUT_URL: PropTypes.string.isRequired, - REFRESH_ACCESS_TOKEN_ENDPOINT: PropTypes.string.isRequired, + REFRESH_ACCESS_TOKEN_API_PATH: PropTypes.string.isRequired, ACCESS_TOKEN_COOKIE_NAME: PropTypes.string.isRequired, CSRF_TOKEN_API_PATH: PropTypes.string.isRequired, }).isRequired, @@ -207,7 +206,7 @@ export function getAuthenticatedUser() { */ export function setAuthenticatedUser(authUser) { service.setAuthenticatedUser(authUser); - publish(AUTHENTICATED_USER_CHANGED); + global.PubSub.publish(AUTHENTICATED_USER_CHANGED); } /** @@ -248,7 +247,7 @@ export async function ensureAuthenticatedUser(redirectUrl) { */ export async function hydrateAuthenticatedUser() { await service.hydrateAuthenticatedUser(); - publish(AUTHENTICATED_USER_CHANGED); + global.PubSub.publish(AUTHENTICATED_USER_CHANGED); } /** diff --git a/runtime/config.js b/runtime/config.js deleted file mode 100644 index 41b20825..00000000 --- a/runtime/config.js +++ /dev/null @@ -1,327 +0,0 @@ -/** - * #### Import members from **@edx/frontend-base** - * - * The configuration module provides utilities for working with an application's configuration - * document (ConfigDocument). Configuration variables can be supplied to the - * application in four different ways. They are applied in the following order: - * - * - Build-time Configuration - * - Environment Variables - * - JavaScript File - * - Runtime Configuration - * - * Last one in wins. Variables with the same name defined via the later methods will override any - * defined using an earlier method. i.e., if a variable is defined in Runtime Configuration, that - * will override the same variable defined in either Build-time Configuration method (environment - * variables or JS file). Configuration defined in a JS file will override environment variables. - * - * ##### Build-time Configuration - * - * Build-time configuration methods add config variables into the app when it is built by webpack. - * This saves the app an API call and means it has all the information it needs to initialize right - * away. There are two methods of supplying build-time configuration: environment variables and a - * JavaScript file. - * - * ###### Environment Variables - * - * A set list of required config variables can be supplied as - * command-line environment variables during the build process. - * - * As a simple example, these are supplied on the command-line before invoking `npm run build`: - * - * ``` - * LMS_BASE_URL=http://localhost:18000 npm run build - * ``` - * - * Note that additional variables _cannot_ be supplied via this method without using the `config` - * initialization handler. The app won't pick them up and they'll appear `undefined`. - * - * This configuration method is being deprecated in favor of JavaScript File Configuration. - * - * ###### JavaScript File Configuration - * - * Configuration variables can be supplied in an optional file named env.config.js. This file must - * export either an Object containing configuration variables or a function. The function must - * return an Object containing configuration variables or, alternately, a promise which resolves to - * an Object. - * - * Using a function or async function allows the configuration to be resolved at runtime (because - * the function will be executed at runtime). This is not common, and the capability is included - * for the sake of flexibility. - * - * JavaScript File Configuration is well-suited to extensibility use cases or component overrides, - * in that the configuration file can depend on any installed JavaScript module. It is also the - * preferred way of doing build-time configuration if runtime configuration isn't used by your - * deployment of the platform. - * - * Exporting a config object: - * ``` - * const config = { - * LMS_BASE_URL: 'http://localhost:18000' - * }; - * - * export default config; - * ``` - * - * Exporting a function that returns an object: - * ``` - * function getConfig() { - * return { - * LMS_BASE_URL: 'http://localhost:18000' - * }; - * } - * ``` - * - * Exporting a function that returns a promise that resolves to an object: - * ``` - * function getAsyncConfig() { - * return new Promise((resolve, reject) => { - * resolve({ - * LMS_BASE_URL: 'http://localhost:18000' - * }); - * }); - * } - * - * export default getAsyncConfig; - * ``` - * - * ##### Runtime Configuration - * - * Configuration variables can also be supplied using the "runtime configuration" method, taking - * advantage of the Micro-frontend Config API in edx-platform. More information on this API can be - * found in the ADR which introduced it: - * - * https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/mfe_config_api/docs/decisions/0001-mfe-config-api.rst - * - * The runtime configuration method can be enabled by supplying a MFE_CONFIG_API_URL via one of the other - * two configuration methods above. - * - * Runtime configuration is particularly useful if you need to supply different configurations to - * a single deployment of a micro-frontend, for instance. It is also a perfectly valid alternative - * to build-time configuration, though it introduces an additional API call to edx-platform on MFE - * initialization. - * - * ##### Initialization Config Handler - * - * The configuration document can be extended by - * applications at run-time using a `config` initialization handler. Please see the Initialization - * documentation for more information on handlers and initialization phases. - * - * ``` - * initialize({ - * handlers: { - * config: () => { - * mergeConfig({ - * CUSTOM_VARIABLE: 'custom value', - * LMS_BASE_URL: 'http://localhost:18001' // You can override variables, but this is uncommon. - * }, 'App config override handler'); - * }, - * }, - * }); - * ``` - * - * @module Config - */ - -import { APP_CONFIG_INITIALIZED, CONFIG_CHANGED } from './constants'; - -import { publish, subscribe } from './pubSub'; -import { ensureDefinedConfig } from './utils'; - -function extractRegex(envVar) { - // Convert the environment variable string to a regex, while guarding - // against a non-string and an empty/whitespace-only string. - if (typeof envVar === 'string' && envVar.trim() !== '') { - return new RegExp(envVar); - } - return undefined; -} - -const ENVIRONMENT = process.env.NODE_ENV; -let config = { - ACCESS_TOKEN_COOKIE_NAME: process.env.ACCESS_TOKEN_COOKIE_NAME, - ACCOUNT_PROFILE_URL: process.env.ACCOUNT_PROFILE_URL, - ACCOUNT_SETTINGS_URL: process.env.ACCOUNT_SETTINGS_URL, - BASE_URL: process.env.BASE_URL, - PUBLIC_PATH: process.env.PUBLIC_PATH || '/', - CREDENTIALS_BASE_URL: process.env.CREDENTIALS_BASE_URL, - CSRF_TOKEN_API_PATH: process.env.CSRF_TOKEN_API_PATH, - DISCOVERY_API_BASE_URL: process.env.DISCOVERY_API_BASE_URL, - PUBLISHER_BASE_URL: process.env.PUBLISHER_BASE_URL, - ECOMMERCE_BASE_URL: process.env.ECOMMERCE_BASE_URL, - ENVIRONMENT, - IGNORED_ERROR_REGEX: extractRegex(process.env.IGNORED_ERROR_REGEX), - LANGUAGE_PREFERENCE_COOKIE_NAME: process.env.LANGUAGE_PREFERENCE_COOKIE_NAME, - LEARNING_BASE_URL: process.env.LEARNING_BASE_URL, - LMS_BASE_URL: process.env.LMS_BASE_URL, - LOGIN_URL: process.env.LOGIN_URL, - LOGOUT_URL: process.env.LOGOUT_URL, - STUDIO_BASE_URL: process.env.STUDIO_BASE_URL, - MARKETING_SITE_BASE_URL: process.env.MARKETING_SITE_BASE_URL, - ORDER_HISTORY_URL: process.env.ORDER_HISTORY_URL, - REFRESH_ACCESS_TOKEN_ENDPOINT: process.env.REFRESH_ACCESS_TOKEN_ENDPOINT, - SECURE_COOKIES: ENVIRONMENT !== 'development', - SEGMENT_KEY: process.env.SEGMENT_KEY, - SITE_NAME: process.env.SITE_NAME, - USER_INFO_COOKIE_NAME: process.env.USER_INFO_COOKIE_NAME, - LOGO_URL: process.env.LOGO_URL, - LOGO_TRADEMARK_URL: process.env.LOGO_TRADEMARK_URL, - LOGO_WHITE_URL: process.env.LOGO_WHITE_URL, - FAVICON_URL: process.env.FAVICON_URL, - MFE_CONFIG_API_URL: process.env.MFE_CONFIG_API_URL, - APP_ID: process.env.APP_ID, - SUPPORT_URL: process.env.SUPPORT_URL, -}; - -/** - * Getter for the application configuration document. This is synchronous and merely returns a - * reference to an existing object, and is thus safe to call as often as desired. - * - * Example: - * - * ``` - * import { getConfig } from '@openedx/frontend-base'; - * - * const { - * LMS_BASE_URL, - * } = getConfig(); - * ``` - * - * @returns {ConfigDocument} - */ -export function getConfig() { - return config; -} - -/** - * Replaces the existing ConfigDocument. This is not commonly used, but can be helpful for tests. - * - * The supplied config document will be tested with `ensureDefinedConfig` to ensure it does not - * have any `undefined` keys. - * - * Example: - * - * ``` - * import { setConfig } from '@openedx/frontend-base'; - * - * setConfig({ - * LMS_BASE_URL, // This is overriding the ENTIRE document - this is not merged in! - * }); - * ``` - * - * @param {ConfigDocument} newConfig - */ -export function setConfig(newConfig) { - ensureDefinedConfig(config, 'config'); - config = newConfig; - publish(CONFIG_CHANGED); -} - -/** - * Merges additional configuration values into the ConfigDocument returned by `getConfig`. Will - * override any values that exist with the same keys. - * - * ``` - * mergeConfig({ - * NEW_KEY: 'new value', - * OTHER_NEW_KEY: 'other new value', - * }); - * - * If any of the key values are `undefined`, an error will be logged to 'warn'. - * - * @param {Object} newConfig - */ -export function mergeConfig(newConfig) { - ensureDefinedConfig(newConfig, 'ProcessEnvConfigService'); - config = Object.assign(config, newConfig); - publish(CONFIG_CHANGED); -} - -/** - * A method allowing application code to indicate that particular ConfigDocument keys are required - * for them to function. This is useful for diagnosing development/deployment issues, primarily, - * by surfacing misconfigurations early. For instance, if the build process fails to supply an - * environment variable on the command-line, it's possible that one of the `process.env` variables - * will be undefined. Should be used in conjunction with `mergeConfig` for custom `ConfigDocument` - * properties. Requester is for informational/error reporting purposes only. - * - * ``` - * ensureConfig(['LMS_BASE_URL', 'LOGIN_URL'], 'MySpecialComponent'); - * - * // Will log a warning with: - * // "App configuration error: LOGIN_URL is required by MySpecialComponent." - * // if LOGIN_URL is undefined, for example. - * ``` - * - * *NOTE*: `ensureConfig` waits until `APP_CONFIG_INITIALIZED` is published to verify the existence - * of the specified properties. This means that this function is compatible with custom `config` - * phase handlers responsible for loading additional configuration data in the initialization - * sequence. - * - * @param {Array} keys - * @param {string} [requester='unspecified application code'] - */ -export function ensureConfig(keys, requester = 'unspecified application code') { - subscribe(APP_CONFIG_INITIALIZED, () => { - keys.forEach((key) => { - if (config[key] === undefined) { - // eslint-disable-next-line no-console - console.warn(`App configuration error: ${key} is required by ${requester}.`); - } - }); - }); -} - -/** - * An object describing the current application configuration. - * - * In its most basic form, the initialization process loads this document via `process.env` - * variables. There are other ways to add configuration variables to the ConfigDocument as - * documented above (JavaScript File Configuration, Runtime Configuration, and the Initialization - * Config Handler) - * - * ``` - * { - * BASE_URL: process.env.BASE_URL, - * // ... other vars - * } - * ``` - * - * When using Webpack (i.e., normal usage), the build process is responsible for supplying these - * variables via command-line environment variables. That means they must be supplied at build - * time. - * - * @name ConfigDocument - * @memberof module:Config - * @property {string} ACCESS_TOKEN_COOKIE_NAME - * @property {string} ACCOUNT_PROFILE_URL - * @property {string} ACCOUNT_SETTINGS_URL - * @property {string} BASE_URL The URL of the current application. - * @property {string} CREDENTIALS_BASE_URL - * @property {string} CSRF_TOKEN_API_PATH - * @property {string} DISCOVERY_API_BASE_URL - * @property {string} PUBLISHER_BASE_URL - * @property {string} ECOMMERCE_BASE_URL - * @property {string} ENVIRONMENT This is one of: development, production, or test. - * @property {string} IGNORED_ERROR_REGEX - * @property {string} LANGUAGE_PREFERENCE_COOKIE_NAME - * @property {string} LEARNING_BASE_URL - * @property {string} LMS_BASE_URL - * @property {string} LOGIN_URL - * @property {string} LOGOUT_URL - * @property {string} STUDIO_BASE_URL - * @property {string} MARKETING_SITE_BASE_URL - * @property {string} ORDER_HISTORY_URL - * @property {string} REFRESH_ACCESS_TOKEN_ENDPOINT - * @property {boolean} SECURE_COOKIES - * @property {string} SEGMENT_KEY - * @property {string} SITE_NAME - * @property {string} USER_INFO_COOKIE_NAME - * @property {string} LOGO_URL - * @property {string} LOGO_TRADEMARK_URL - * @property {string} LOGO_WHITE_URL - * @property {string} FAVICON_URL - * @property {string} MFE_CONFIG_API_URL - * @property {string} APP_ID - * @property {string} SUPPORT_URL - */ diff --git a/runtime/config.ts b/runtime/config.ts new file mode 100644 index 00000000..9d2c697c --- /dev/null +++ b/runtime/config.ts @@ -0,0 +1,246 @@ +/** + * #### Import members from **@edx/frontend-base** + * + * The configuration module provides utilities for working with an application's configuration + * document (ConfigDocument). Configuration variables can be supplied to the + * application in three different ways. They are applied in the following order: + * + * - Site Configuration File (site.config.tsx) + * - Initialization Config Handler + * - Runtime Configuration + * + * Last one in wins, and are deep merged together. Variables with the same name defined via the + * later methods will override any defined using an earlier method. i.e., if a variable is defined + * in Runtime Configuration, that will override the same variable defined in either of the earlier + * methods. Configuration defined in a JS file will override any default values below. + * + * ##### Site Configuration File + * + * Configuration variables can be supplied in a file named site.config.tsx. This file must + * export either an Object containing configuration variables or a function. The function must + * return an Object containing configuration variables or, alternately, a promise which resolves to + * an Object. + * + * Using a function or async function allows the configuration to be resolved at runtime (because + * the function will be executed at runtime). This is not common, and the capability is included + * for the sake of flexibility. + * + * The Site Configuration File is well-suited to extensibility use cases or component overrides, + * in that the configuration file can depend on any installed JavaScript module. It is also the + * preferred way of doing build-time configuration if runtime configuration isn't used by your + * deployment of the platform. + * + * Exporting a config object: + * ``` + * const config = { + * LMS_BASE_URL: 'http://localhost:18000' + * }; + * + * export default config; + * ``` + * + * Exporting a function that returns an object: + * ``` + * function getConfig() { + * return { + * LMS_BASE_URL: 'http://localhost:18000' + * }; + * } + * ``` + * + * Exporting a function that returns a promise that resolves to an object: + * ``` + * function getAsyncConfig() { + * return new Promise((resolve, reject) => { + * resolve({ + * LMS_BASE_URL: 'http://localhost:18000' + * }); + * }); + * } + * + * export default getAsyncConfig; + * ``` + * + * ##### Initialization Config Handler + * + * The configuration document can be extended by + * applications at run-time using a `config` initialization handler. Please see the Initialization + * documentation for more information on handlers and initialization phases. + * + * ``` + * initialize({ + * handlers: { + * config: () => { + * mergeConfig({ + * CUSTOM_VARIABLE: 'custom value', + * LMS_BASE_URL: 'http://localhost:18001' // You can override variables, but this is uncommon. + * }, 'App config override handler'); + * }, + * }, + * }); + * ``` + * + * ##### Runtime Configuration + * + * Configuration variables can also be supplied using the "runtime configuration" method, taking + * advantage of the Micro-frontend Config API in edx-platform. More information on this API can be + * found in the ADR which introduced it: + * + * https://github.com/openedx/edx-platform/blob/master/lms/djangoapps/mfe_config_api/docs/decisions/0001-mfe-config-api.rst + * + * The runtime configuration method can be enabled by supplying a MFE_CONFIG_API_URL via one of the other + * two configuration methods above. + * + * Runtime configuration is particularly useful if you need to supply different configurations to + * a single deployment of a micro-frontend, for instance. It is also a perfectly valid alternative + * to build-time configuration, though it introduces an additional API call to edx-platform on MFE + * initialization. + * + * + * @module Config + */ + +import merge from 'lodash.merge'; +import 'pubsub-js'; +import { SiteConfig } from '../types'; +import { CONFIG_CHANGED } from './constants'; + +let config: SiteConfig = { + ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload', + CSRF_TOKEN_API_PATH: '/csrf/api/v1/token', + ENVIRONMENT: 'production', + IGNORED_ERROR_REGEX: null, + LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference', + PUBLIC_PATH: '/', + REFRESH_ACCESS_TOKEN_API_PATH: '/login_refresh', + USER_INFO_COOKIE_NAME: 'edx-user-info', + ORDER_HISTORY_URL: null, + MFE_CONFIG_API_URL: null, + SUPPORT_URL: null, + SEGMENT_KEY: null, + + apps: [], + + APP_ID: '', + BASE_URL: '', + SITE_NAME: '', + ACCOUNT_PROFILE_URL: '', + ACCOUNT_SETTINGS_URL: '', + LEARNING_BASE_URL: '', + LOGIN_URL: '', + LOGOUT_URL: '', + MARKETING_SITE_BASE_URL: '', + CREDENTIALS_BASE_URL: '', + DISCOVERY_API_BASE_URL: '', + ECOMMERCE_BASE_URL: '', + LMS_BASE_URL: '', + PUBLISHER_BASE_URL: '', + STUDIO_BASE_URL: '', + FAVICON_URL: '', + LOGO_TRADEMARK_URL: '', + LOGO_URL: '', + LOGO_WHITE_URL: '' +}; + +/** + * Getter for the application configuration document. This is synchronous and merely returns a + * reference to an existing object, and is thus safe to call as often as desired. + * + * Example: + * + * ``` + * import { getConfig } from '@openedx/frontend-base'; + * + * const { + * LMS_BASE_URL, + * } = getConfig(); + * ``` + * + * @returns {ConfigDocument} + */ +export function getConfig() { + return config; +} + +/** + * Replaces the existing ConfigDocument. This is not commonly used, but can be helpful for tests. + * + * Example: + * + * ``` + * import { setConfig } from '@openedx/frontend-base'; + * + * setConfig({ + * LMS_BASE_URL, // This is overriding the ENTIRE document - this is not merged in! + * }); + * ``` + * + * @param {ConfigDocument} newConfig + */ +export function setConfig(newConfig: SiteConfig) { + config = newConfig; + global.PubSub.publish(CONFIG_CHANGED); +} + +/** + * Merges additional configuration values into the site config returned by `getConfig`. Will + * override any values that exist with the same keys. + * + * ``` + * mergeConfig({ + * NEW_KEY: 'new value', + * OTHER_NEW_KEY: 'other new value', + * }); + * + * This function uses lodash.merge internally to merge configuration objects + * which means they will be merged recursively. See https://lodash.com/docs/latest#merge for + * documentation on the exact behavior. + * + * @param {Object} newConfig + */ +export function mergeConfig(newConfig: Partial) { + config = merge(config, newConfig); + global.PubSub.publish(CONFIG_CHANGED); +} + +/** + * An object describing the current application configuration. + * + * In its most basic form, this document contains some sensible defaults. The initialization + * process then merges additional configuration into this document using the configuration methods + * described above. (Site configuration file, initialization config handler, and runtime + * configuration) + * + * @name ConfigDocument + * @memberof module:Config + * @property {string} ACCESS_TOKEN_COOKIE_NAME + * @property {string} ACCOUNT_PROFILE_URL + * @property {string} ACCOUNT_SETTINGS_URL + * @property {string} BASE_URL The URL of the current application. + * @property {string} CREDENTIALS_BASE_URL + * @property {string} CSRF_TOKEN_API_PATH + * @property {string} DISCOVERY_API_BASE_URL + * @property {string} PUBLISHER_BASE_URL + * @property {string} ECOMMERCE_BASE_URL + * @property {string} ENVIRONMENT This is one of: development, production, or test. + * @property {string} IGNORED_ERROR_REGEX + * @property {string} LANGUAGE_PREFERENCE_COOKIE_NAME + * @property {string} LEARNING_BASE_URL + * @property {string} LMS_BASE_URL + * @property {string} LOGIN_URL + * @property {string} LOGOUT_URL + * @property {string} STUDIO_BASE_URL + * @property {string} MARKETING_SITE_BASE_URL + * @property {string} ORDER_HISTORY_URL + * @property {string} REFRESH_ACCESS_TOKEN_API_PATH + * @property {string} SEGMENT_KEY + * @property {string} SITE_NAME + * @property {string} USER_INFO_COOKIE_NAME + * @property {string} LOGO_URL + * @property {string} LOGO_TRADEMARK_URL + * @property {string} LOGO_WHITE_URL + * @property {string} FAVICON_URL + * @property {string} MFE_CONFIG_API_URL + * @property {string} APP_ID + * @property {string} SUPPORT_URL + */ diff --git a/runtime/i18n/injectIntlWithShim.jsx b/runtime/i18n/injectIntlWithShim.jsx index 17ace606..a26419df 100644 --- a/runtime/i18n/injectIntlWithShim.jsx +++ b/runtime/i18n/injectIntlWithShim.jsx @@ -1,6 +1,7 @@ import React from 'react'; import { injectIntl } from 'react-intl'; import { getLoggingService, intlShape } from './lib'; +import { getConfig } from '../config'; /** * This function wraps react-intl's injectIntl function in order to add error logging to the intl @@ -17,7 +18,7 @@ const injectIntlWithShim = (WrappedComponent) => { value: (definition, ...args) => { if (definition === undefined || definition.id === undefined) { const error = new Error('i18n error: An undefined message was supplied to intl.formatMessage.'); - if (process.env.NODE_ENV !== 'production') { + if (getConfig().ENVIRONMENT !== 'production') { console.error(error); // eslint-disable-line no-console return '!!! Missing message supplied to intl.formatMessage !!!'; } diff --git a/runtime/index.js b/runtime/index.js index 48a088cd..37371af1 100644 --- a/runtime/index.js +++ b/runtime/index.js @@ -32,7 +32,6 @@ export { } from './auth'; export { - ensureConfig, getConfig, mergeConfig, setConfig @@ -82,7 +81,7 @@ export { export { auth, - history, + getHistory, initError, initialize } from './initialize'; @@ -98,11 +97,11 @@ export { } from './logging'; export { - Plugin, - PluginSlot, - PLUGIN_OPERATIONS, - IFRAME_PLUGIN, DIRECT_PLUGIN, + IFRAME_PLUGIN, + PLUGIN_OPERATIONS, + Plugin, + PluginSlot } from './plugins'; export { @@ -130,7 +129,6 @@ export { export { camelCaseObject, convertKeyNames, - ensureDefinedConfig, getPath, getQueryParameters, modifyObjectKeys, diff --git a/runtime/initialize.async.function.config.test.js b/runtime/initialize.async.function.config.test.js index 4ce61f02..323bf1cc 100644 --- a/runtime/initialize.async.function.config.test.js +++ b/runtime/initialize.async.function.config.test.js @@ -1,3 +1,4 @@ +import 'pubsub-js'; import { initialize } from './initialize'; import { @@ -15,7 +16,7 @@ jest.mock('./auth'); jest.mock('./analytics'); jest.mock('./i18n'); jest.mock('./auth/LocalForageCache'); -jest.mock('env.config.js', () => async () => new Promise((resolve) => { +jest.mock('site.config', () => async () => new Promise((resolve) => { resolve({ JS_FILE_VAR: 'JS_FILE_VAR_VALUE_ASYNC_FUNCTION', }); diff --git a/runtime/initialize.const.config.test.js b/runtime/initialize.const.config.test.js index db19c870..3f30c90c 100644 --- a/runtime/initialize.const.config.test.js +++ b/runtime/initialize.const.config.test.js @@ -1,3 +1,4 @@ +import 'pubsub-js'; import { initialize } from './initialize'; import { @@ -15,7 +16,7 @@ jest.mock('./auth'); jest.mock('./analytics'); jest.mock('./i18n'); jest.mock('./auth/LocalForageCache'); -jest.mock('env.config.js', () => ({ +jest.mock('site.config', () => ({ JS_FILE_VAR: 'JS_FILE_VAR_VALUE_CONSTANT', })); diff --git a/runtime/initialize.function.config.test.js b/runtime/initialize.function.config.test.js index 3b1a8498..4683754e 100644 --- a/runtime/initialize.function.config.test.js +++ b/runtime/initialize.function.config.test.js @@ -1,4 +1,4 @@ -import { initialize } from './initialize'; +import 'pubsub-js'; import { ensureAuthenticatedUser, @@ -6,6 +6,7 @@ import { hydrateAuthenticatedUser, } from './auth'; import { getConfig } from './config'; +import { initialize } from './initialize'; import { logError, } from './logging'; @@ -15,7 +16,7 @@ jest.mock('./auth'); jest.mock('./analytics'); jest.mock('./i18n'); jest.mock('./auth/LocalForageCache'); -jest.mock('env.config.js', () => () => ({ +jest.mock('site.config', () => () => ({ JS_FILE_VAR: 'JS_FILE_VAR_VALUE_FUNCTION', })); diff --git a/runtime/initialize.js b/runtime/initialize.js index da8135ba..7aa66f5c 100644 --- a/runtime/initialize.js +++ b/runtime/initialize.js @@ -52,15 +52,10 @@ import { createMemoryHistory } from 'history'; /* -This 'env.config' package is a special 'magic' alias in our webpack configuration in the `config` -folder. It points at an `env.config.js` file in the root of an MFE's repository if it exists and -falls back to an empty object `{}` if the file doesn't exist. This acts like an 'optional' import, -in a sense. +This 'site.config' package is a special 'magic' alias in our webpack configuration in the `config` +folder. It points at an `site.config.tsx` file in the root of an project's repository. */ -import envConfig from 'env.config'; // eslint-disable-line import/no-unresolved -import { - publish, -} from './pubSub'; +import siteConfig from 'site.config'; import { getPath } from './utils'; // eslint-disable-next-line import/no-cycle import { @@ -80,7 +75,7 @@ import { } from './auth'; import configureCache from './auth/LocalForageCache'; import { - getConfig, mergeConfig, + getConfig, mergeConfig } from './config'; import { APP_ANALYTICS_INITIALIZED, @@ -108,10 +103,9 @@ import { GoogleAnalyticsLoader } from './scripts'; * in environments where browser history may be inaccessible due to `window` being undefined, this * falls back to memory history. */ -export const history = (typeof window !== 'undefined') - ? createBrowserHistory({ - basename: getPath(getConfig().PUBLIC_PATH), - }) : createMemoryHistory(); +export const getHistory = () => ((typeof window !== 'undefined') + ? createBrowserHistory({ basename: getPath(getConfig().PUBLIC_PATH) }) + : createMemoryHistory()); /** * The string basename that is the root directory of this MFE. @@ -123,7 +117,7 @@ export const history = (typeof window !== 'undefined') * as an ENV variable in the Docker file, and we read it here from that configuration so that it * can be passed into a Router later. */ -export const basename = getPath(getConfig().PUBLIC_PATH); +export const getBasename = () => getPath(getConfig().PUBLIC_PATH); /** * The default handler for the initialization lifecycle's `initError` phase. Logs the error to the @@ -165,19 +159,19 @@ export async function auth(requireUser, hydrateUser) { } /** - * Set or overrides configuration via an env.config.js file in the consuming application. - * This env.config.js is loaded at runtime and must export one of two things: + * Set or overrides configuration via an site.config.tsx file in the consuming application. + * This site.config.tsx is loaded at runtime and must export one of two things: * * - An object which will be merged into the application config via `mergeConfig`. * - A function which returns an object which will be merged into the application config via * `mergeConfig`. This function can return a promise. */ -async function jsFileConfig() { +async function fileConfig() { let config = {}; - if (typeof envConfig === 'function') { - config = await envConfig(); + if (typeof siteConfig === 'function') { + config = await siteConfig(); } else { - config = envConfig; + config = siteConfig; } mergeConfig(config); @@ -311,13 +305,13 @@ export async function initialize({ try { // Pub/Sub await handlers.pubSub(); - publish(APP_PUBSUB_INITIALIZED); + global.PubSub.publish(APP_PUBSUB_INITIALIZED); // Configuration + await fileConfig(); await handlers.config(); - await jsFileConfig(); await runtimeConfig(); - publish(APP_CONFIG_INITIALIZED); + global.PubSub.publish(APP_CONFIG_INITIALIZED); loadExternalScripts(externalScripts, { config: getConfig(), @@ -337,7 +331,7 @@ export async function initialize({ config: getConfig(), }); await handlers.logging(); - publish(APP_LOGGING_INITIALIZED); + global.PubSub.publish(APP_LOGGING_INITIALIZED); // Internationalization configureI18n({ @@ -346,7 +340,7 @@ export async function initialize({ loggingService: getLoggingService(), }); await handlers.i18n(); - publish(APP_I18N_INITIALIZED); + global.PubSub.publish(APP_I18N_INITIALIZED); // Authentication configureAuth(authServiceImpl, { @@ -356,7 +350,7 @@ export async function initialize({ }); await handlers.auth(requireUser, hydrateUser); - publish(APP_AUTH_INITIALIZED); + global.PubSub.publish(APP_AUTH_INITIALIZED); // Analytics configureAnalytics(analyticsServiceImpl, { @@ -365,16 +359,16 @@ export async function initialize({ httpClient: getAuthenticatedHttpClient(), }); await handlers.analytics(); - publish(APP_ANALYTICS_INITIALIZED); + global.PubSub.publish(APP_ANALYTICS_INITIALIZED); // Application Ready await handlers.ready(); - publish(APP_READY); + global.PubSub.publish(APP_READY); } catch (error) { if (!error.isRedirecting) { // Initialization Error await handlers.initError(error); - publish(APP_INIT_ERROR, error); + global.PubSub.publish(APP_INIT_ERROR, error); } } } diff --git a/runtime/initialize.test.js b/runtime/initialize.test.js index 8fd84f88..bae7a535 100644 --- a/runtime/initialize.test.js +++ b/runtime/initialize.test.js @@ -1,4 +1,5 @@ import { createBrowserHistory } from 'history'; +import 'pubsub-js'; import { APP_ANALYTICS_INITIALIZED, APP_AUTH_INITIALIZED, @@ -9,8 +10,7 @@ import { APP_PUBSUB_INITIALIZED, APP_READY, } from './constants'; -import { initialize } from './initialize'; -import { subscribe } from './pubSub'; +import { getHistory, initialize } from './initialize'; import { configure as configureAnalytics, SegmentAnalyticsService } from './analytics'; import { @@ -23,7 +23,7 @@ import { hydrateAuthenticatedUser, } from './auth'; import configureCache from './auth/LocalForageCache'; -import { getConfig } from './config'; +import { getConfig, mergeConfig } from './config'; import { configure as configureI18n } from './i18n'; import { configure as configureLogging, @@ -60,7 +60,7 @@ const newConfig = { STUDIO_BASE_URL: 'http://studio.example.com:18010', MARKETING_SITE_BASE_URL: 'http://test.example.com:18000', ORDER_HISTORY_URL: 'http://test.example.com:1996/orders', - REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://test.example.com:18000/login_refresh', + REFRESH_ACCESS_TOKEN_API_PATH: '/login_refresh', SEGMENT_KEY: '', USER_INFO_COOKIE_NAME: 'edx-user-info', IGNORED_ERROR_REGEX: '', @@ -106,13 +106,13 @@ describe('initialize', () => { } } - subscribe(APP_PUBSUB_INITIALIZED, checkDispatchedDone); - subscribe(APP_CONFIG_INITIALIZED, checkDispatchedDone); - subscribe(APP_LOGGING_INITIALIZED, checkDispatchedDone); - subscribe(APP_AUTH_INITIALIZED, checkDispatchedDone); - subscribe(APP_ANALYTICS_INITIALIZED, checkDispatchedDone); - subscribe(APP_I18N_INITIALIZED, checkDispatchedDone); - subscribe(APP_READY, checkDispatchedDone); + global.PubSub.subscribe(APP_PUBSUB_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_CONFIG_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_LOGGING_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_AUTH_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_ANALYTICS_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_I18N_INITIALIZED, checkDispatchedDone); + global.PubSub.subscribe(APP_READY, checkDispatchedDone); const messages = { i_am: 'a message' }; await initialize({ messages }); @@ -222,7 +222,7 @@ describe('initialize', () => { expect(data).toEqual(new Error('uhoh!')); } - subscribe(APP_INIT_ERROR, errorHandler); + global.PubSub.subscribe(APP_INIT_ERROR, errorHandler); await initialize({ messages: null, @@ -258,7 +258,7 @@ describe('initialize', () => { expect(data).toEqual(new Error('uhoh!')); } - subscribe(APP_INIT_ERROR, errorHandler); + global.PubSub.subscribe(APP_INIT_ERROR, errorHandler); await initialize({ messages: null, @@ -276,8 +276,6 @@ describe('initialize', () => { }); it('should initialize the app with runtime configuration', async () => { - config.MFE_CONFIG_API_URL = 'http://localhost:18000/api/mfe/v1/config'; - config.APP_ID = 'auth'; configureCache.mockReturnValueOnce(Promise.resolve({ get: (url) => { const params = new URL(url).search; @@ -287,7 +285,17 @@ describe('initialize', () => { })); const messages = { i_am: 'a message' }; - await initialize({ messages }); + await initialize({ + messages, + handlers: { + config: () => { + mergeConfig({ + MFE_CONFIG_API_URL: 'http://localhost:18000/api/mfe/v1/config', + APP_ID: 'auth', + }); + } + } + }); expect(configureCache).toHaveBeenCalled(); expect(configureLogging).toHaveBeenCalledWith(NewRelicLoggingService, { config }); @@ -311,50 +319,65 @@ describe('initialize', () => { expect(ensureAuthenticatedUser).not.toHaveBeenCalled(); expect(hydrateAuthenticatedUser).not.toHaveBeenCalled(); expect(logError).not.toHaveBeenCalled(); - expect(config.SITE_NAME).toBe(newConfig.common.SITE_NAME); - expect(config.INFO_EMAIL).toBe(newConfig.auth.INFO_EMAIL); - expect(Object.values(config).includes(newConfig.learning.DISCUSSIONS_MFE_BASE_URL)).toBeFalsy(); + expect(getConfig().SITE_NAME).toBe(newConfig.common.SITE_NAME); + expect(getConfig().INFO_EMAIL).toBe(newConfig.auth.INFO_EMAIL); + expect(Object.values(getConfig()).includes(newConfig.learning.DISCUSSIONS_MFE_BASE_URL)).toBeFalsy(); }); - it('should initialize the app with the build config when runtime configuration fails', async () => { - config.MFE_CONFIG_API_URL = 'http://localhost:18000/api/mfe/v1/config'; - // eslint-disable-next-line no-console - console.error = jest.fn(); - configureCache.mockReturnValueOnce(Promise.reject(new Error('Api fails'))); - - const messages = { i_am: 'a message' }; - await initialize({ - messages, + describe('with mocked console.error', () => { + beforeEach(() => { + console.error = jest.fn(); }); - expect(configureCache).toHaveBeenCalled(); - // eslint-disable-next-line no-console - expect(console.error).toHaveBeenCalledWith('Error with config API', 'Api fails'); - expect(configureLogging).toHaveBeenCalledWith(NewRelicLoggingService, { config }); - expect(configureAuth).toHaveBeenCalledWith(AxiosJwtAuthService, { - loggingService: getLoggingService(), - config, - middleware: [], + afterAll(() => { + console.error.mockRestore(); }); - expect(configureAnalytics).toHaveBeenCalledWith(SegmentAnalyticsService, { - config, - loggingService: getLoggingService(), - httpClient: getAuthenticatedHttpClient(), - }); - expect(configureI18n).toHaveBeenCalledWith({ - messages, - config, - loggingService: getLoggingService(), + + it('should initialize the app with the build config when runtime configuration fails', async () => { + configureCache.mockRejectedValueOnce(new Error('Api fails')); + + const messages = { i_am: 'a message' }; + await initialize({ + messages, + handlers: { + config: () => { + mergeConfig({ + MFE_CONFIG_API_URL: 'http://localhost:18000/api/mfe/v1/config', + APP_ID: 'auth', + }); + } + } + }); + + expect(configureCache).toHaveBeenCalled(); + expect(console.error).toHaveBeenCalledWith('Error with config API', 'Api fails'); + expect(configureLogging).toHaveBeenCalledWith(NewRelicLoggingService, { config }); + expect(configureAuth).toHaveBeenCalledWith(AxiosJwtAuthService, { + loggingService: getLoggingService(), + config, + middleware: [], + }); + expect(configureAnalytics).toHaveBeenCalledWith(SegmentAnalyticsService, { + config, + loggingService: getLoggingService(), + httpClient: getAuthenticatedHttpClient(), + }); + expect(configureI18n).toHaveBeenCalledWith({ + messages, + config, + loggingService: getLoggingService(), + }); + expect(fetchAuthenticatedUser).toHaveBeenCalled(); + expect(ensureAuthenticatedUser).not.toHaveBeenCalled(); + expect(hydrateAuthenticatedUser).not.toHaveBeenCalled(); + expect(logError).not.toHaveBeenCalled(); }); - expect(fetchAuthenticatedUser).toHaveBeenCalled(); - expect(ensureAuthenticatedUser).not.toHaveBeenCalled(); - expect(hydrateAuthenticatedUser).not.toHaveBeenCalled(); - expect(logError).not.toHaveBeenCalled(); }); }); describe('history', () => { it('browser history called by default path', async () => { + getHistory(); // import history from initialize; expect(createBrowserHistory).toHaveBeenCalledWith({ basename: '/', diff --git a/runtime/jest.config.js b/runtime/jest.config.js index aa81d66e..206bfef0 100644 --- a/runtime/jest.config.js +++ b/runtime/jest.config.js @@ -6,7 +6,7 @@ module.exports = { '\\.svg$': '/runtime/__mocks__/svg.js', '\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$': '/runtime/__mocks__/file.js', '\\.(css|scss)$': require.resolve('identity-obj-proxy'), - 'env.config': '/runtime/__mocks__/env.config.js', + 'site.config': '/runtime/test.site.config.tsx', }, testEnvironment: 'jsdom', testEnvironmentOptions: { @@ -24,7 +24,9 @@ module.exports = { '/node_modules/(?!(@openedx|@edx)/)', ], modulePathIgnorePatterns: [ - '/dist/', + '/dist', + '/shell', + '/tools', ], testPathIgnorePatterns: [ '/node_modules/', diff --git a/runtime/logging/NewRelicLoggingService.js b/runtime/logging/NewRelicLoggingService.js index d4fb1eb2..a7c89559 100644 --- a/runtime/logging/NewRelicLoggingService.js +++ b/runtime/logging/NewRelicLoggingService.js @@ -1,3 +1,5 @@ +import { getConfig } from '../config'; + /** * NewRelic will not log an error if it is too long. * @@ -22,7 +24,7 @@ const pageActionNameInfo = 'INFO'; const pageActionNameIgnoredError = 'IGNORED_ERROR'; function sendPageAction(actionName, message, customAttributes) { - if (process.env.NODE_ENV === 'development') { + if (getConfig().ENVIRONMENT === 'development') { console.log(actionName, message, customAttributes); // eslint-disable-line } if (window && typeof window.newrelic !== 'undefined') { @@ -32,7 +34,7 @@ function sendPageAction(actionName, message, customAttributes) { } function sendError(error, customAttributes) { - if (process.env.NODE_ENV === 'development') { + if (getConfig().ENVIRONMENT === 'development') { console.error(error, customAttributes); // eslint-disable-line } if (window && typeof window.newrelic !== 'undefined') { @@ -42,7 +44,7 @@ function sendError(error, customAttributes) { } function setCustomAttribute(name, value) { - if (process.env.NODE_ENV === 'development') { + if (getConfig().ENVIRONMENT === 'development') { console.log(name, value); // eslint-disable-line } if (window && typeof window.newrelic !== 'undefined') { diff --git a/runtime/plugins/PluginContainer.jsx b/runtime/plugins/PluginContainer.jsx index 8bd158a7..0ed6629e 100644 --- a/runtime/plugins/PluginContainer.jsx +++ b/runtime/plugins/PluginContainer.jsx @@ -1,15 +1,14 @@ 'use client'; -import React from 'react'; import PropTypes from 'prop-types'; // eslint-disable-next-line import/no-extraneous-dependencies -import PluginContainerIframe from './PluginContainerIframe'; import PluginContainerDirect from './PluginContainerDirect'; +import PluginContainerIframe from './PluginContainerIframe'; import { - IFRAME_PLUGIN, DIRECT_PLUGIN, + IFRAME_PLUGIN, } from './data/constants'; import { pluginConfigShape } from './data/shapes'; diff --git a/runtime/plugins/PluginSlot.test.jsx b/runtime/plugins/PluginSlot.test.jsx index b3ef7e95..6d670a7d 100644 --- a/runtime/plugins/PluginSlot.test.jsx +++ b/runtime/plugins/PluginSlot.test.jsx @@ -67,10 +67,12 @@ const TestPluginSlot = ( describe('PluginSlot', () => { beforeEach(() => { usePluginSlot.mockReturnValue(defaultSlotConfig); + console.error = jest.fn(); }); afterEach(() => { jest.clearAllMocks(); + console.error.mockRestore(); }); it('should render multiple types of Plugin in a single slot config', () => { diff --git a/runtime/pubSub.js b/runtime/pubSub.js index db540ea1..00e5f35d 100644 --- a/runtime/pubSub.js +++ b/runtime/pubSub.js @@ -19,29 +19,8 @@ import PubSub from 'pubsub-js'; -/** - * - * @param {string} type - * @param {function} callback - * @returns {string} A subscription token that can be passed to `unsubscribe` - */ -export function subscribe(type, callback) { - return PubSub.subscribe(type, callback); -} - -/** - * - * @param {string} token A subscription token provided by `subscribe` - */ -export function unsubscribe(token) { - return PubSub.unsubscribe(token); -} - -/** - * - * @param {string} type - * @param {Object} data - */ -export function publish(type, data) { - return PubSub.publish(type, data); -} +export const { + subscribe, + unsubscribe, + publish, +} = PubSub; diff --git a/runtime/react/AppProvider.jsx b/runtime/react/AppProvider.jsx index df8fbab5..46dbccf8 100644 --- a/runtime/react/AppProvider.jsx +++ b/runtime/react/AppProvider.jsx @@ -11,7 +11,7 @@ import { IntlProvider, LOCALE_CHANGED, } from '../i18n'; -import { basename } from '../initialize'; +import { getBasename } from '../initialize'; import AppContext from './AppContext'; import ErrorBoundary from './ErrorBoundary'; @@ -73,7 +73,7 @@ export default function AppProvider({ store, children, wrapWithRouter }) { > {wrapWithRouter ? ( - +
{children}
diff --git a/runtime/react/AppProvider.test.jsx b/runtime/react/AppProvider.test.jsx index af0a7625..a5a2795a 100644 --- a/runtime/react/AppProvider.test.jsx +++ b/runtime/react/AppProvider.test.jsx @@ -1,8 +1,7 @@ -import React from 'react'; -import { createStore } from 'redux'; import { render } from '@testing-library/react'; -import AppProvider from './AppProvider'; +import { createStore } from 'redux'; import { initialize } from '../initialize'; +import AppProvider from './AppProvider'; jest.mock('../auth', () => ({ configure: () => {}, @@ -44,6 +43,8 @@ describe('AppProvider', () => { ru: {}, th: {}, uk: {}, + 'fa-ir': {}, + fa: {}, }, }); }); diff --git a/runtime/react/AuthenticatedPageRoute.test.jsx b/runtime/react/AuthenticatedPageRoute.test.jsx index c629b093..32aa52e2 100644 --- a/runtime/react/AuthenticatedPageRoute.test.jsx +++ b/runtime/react/AuthenticatedPageRoute.test.jsx @@ -1,13 +1,13 @@ /* eslint-disable react/jsx-no-constructed-context-values */ -import React from 'react'; import { render } from '@testing-library/react'; -import { Route, Routes, MemoryRouter } from 'react-router-dom'; +import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { sendPageEvent } from '../analytics'; import { getAuthenticatedUser, getLoginRedirectUrl } from '../auth'; -import AuthenticatedPageRoute from './AuthenticatedPageRoute'; -import AppContext from './AppContext'; import { getConfig } from '../config'; -import { sendPageEvent } from '../analytics'; +import AppContext from './AppContext'; +import AuthenticatedPageRoute from './AuthenticatedPageRoute'; +jest.mock('../pubSub'); jest.mock('../analytics'); jest.mock('../auth'); diff --git a/runtime/react/hooks.js b/runtime/react/hooks.js index 83800cc0..a3b10cb9 100644 --- a/runtime/react/hooks.js +++ b/runtime/react/hooks.js @@ -1,6 +1,5 @@ /* eslint-disable import/prefer-default-export */ import { useEffect } from 'react'; -import { subscribe, unsubscribe } from '../pubSub'; import { sendTrackEvent } from '../analytics'; /** @@ -15,10 +14,10 @@ import { sendTrackEvent } from '../analytics'; */ export const useAppEvent = (type, callback) => { useEffect(() => { - const subscriptionToken = subscribe(type, callback); + const subscriptionToken = global.PubSub.subscribe(type, callback); return function cleanup() { - unsubscribe(subscriptionToken); + global.PubSub.unsubscribe(subscriptionToken); }; }, [callback, type]); }; diff --git a/runtime/setupTest.js b/runtime/setupTest.js index 72a5dbc4..5bae8e8d 100644 --- a/runtime/setupTest.js +++ b/runtime/setupTest.js @@ -1,5 +1,7 @@ /* eslint-disable import/no-extraneous-dependencies */ import '@testing-library/jest-dom'; +import siteConfig from 'site.config'; +import { mergeConfig } from './config'; jest.mock('universal-cookie', () => { const mCookie = { @@ -9,45 +11,4 @@ jest.mock('universal-cookie', () => { return jest.fn(() => mCookie); }); -// These configuration values are usually set in webpack's EnvironmentPlugin however -// Jest does not use webpack so we need to set these so for testing -process.env.ACCESS_TOKEN_COOKIE_NAME = 'edx-jwt-cookie-header-payload'; -process.env.ACCOUNT_PROFILE_URL = 'http://localhost:1995'; -process.env.ACCOUNT_SETTINGS_URL = 'http://localhost:1997'; -process.env.BASE_URL = 'localhost:8080'; -process.env.CREDENTIALS_BASE_URL = 'http://localhost:18150'; -process.env.CSRF_TOKEN_API_PATH = '/csrf/api/v1/token'; -process.env.DISCOVERY_API_BASE_URL = 'http://localhost:18381'; -process.env.PUBLISHER_BASE_URL = 'http://localhost:18400'; -process.env.ECOMMERCE_BASE_URL = 'http://localhost:18130'; -process.env.LANGUAGE_PREFERENCE_COOKIE_NAME = 'openedx-language-preference'; -process.env.LMS_BASE_URL = 'http://localhost:18000'; -process.env.LEARNING_BASE_URL = 'http://localhost:2000'; -process.env.LOGIN_URL = 'http://localhost:18000/login'; -process.env.LOGOUT_URL = 'http://localhost:18000/logout'; -process.env.STUDIO_BASE_URL = 'http://localhost:18010'; -process.env.MARKETING_SITE_BASE_URL = 'http://localhost:18000'; -process.env.ORDER_HISTORY_URL = 'localhost:1996/orders'; -process.env.REFRESH_ACCESS_TOKEN_ENDPOINT = 'http://localhost:18000/login_refresh'; -process.env.SEGMENT_KEY = 'segment_whoa'; -process.env.SITE_NAME = 'edX'; -process.env.USER_INFO_COOKIE_NAME = 'edx-user-info'; -process.env.LOGO_URL = 'https://edx-cdn.org/v3/default/logo.svg'; -process.env.LOGO_TRADEMARK_URL = 'https://edx-cdn.org/v3/default/logo-trademark.svg'; -process.env.LOGO_WHITE_URL = 'https://edx-cdn.org/v3/default/logo-white.svg'; -process.env.FAVICON_URL = 'https://edx-cdn.org/v3/default/favicon.ico'; -process.env.MFE_CONFIG_API_URL = ''; -process.env.APP_ID = ''; -process.env.SUPPORT_URL = 'https://support.edx.org'; - -/* Auth test variables - -process.env.BASE_URL = 'http://example.com'; -process.env.LMS_BASE_URL = 'http://auth.example.com'; -process.env.LOGIN_URL = 'http://auth.example.com/login'; -process.env.LOGOUT_URL = 'http://auth.example.com/logout'; -process.env.REFRESH_ACCESS_TOKEN_ENDPOINT = 'http://auth.example.com/api/refreshToken'; -process.env.ACCESS_TOKEN_COOKIE_NAME = 'access-token-cookie-name'; -process.env.USER_INFO_COOKIE_NAME = 'user-info-cookie-name'; - -*/ +mergeConfig(siteConfig); diff --git a/test-app/env.config.js b/runtime/test.site.config.tsx similarity index 71% rename from test-app/env.config.js rename to runtime/test.site.config.tsx index b26b804a..675d660f 100644 --- a/test-app/env.config.js +++ b/runtime/test.site.config.tsx @@ -1,9 +1,7 @@ -module.exports = { - FALSE_VALUE: false, - CORRECT_BOOL_VALUE: 'Good, false meant false. We did not cast a boolean to a string.', - INCORRECT_BOOL_VALUE: 'Why was a false boolean true?', - INTEGER_VALUE: 123, - EXAMPLE_VAR: 'Example Value', +import { SiteConfig } from '..'; + +const config: SiteConfig = { + apps: [], ACCESS_TOKEN_COOKIE_NAME: 'edx-jwt-cookie-header-payload', ACCOUNT_PROFILE_URL: 'http://localhost:1995', ACCOUNT_SETTINGS_URL: 'http://localhost:1997', @@ -14,23 +12,26 @@ module.exports = { PUBLISHER_BASE_URL: 'http://localhost:18400', ECOMMERCE_BASE_URL: 'http://localhost:18130', LANGUAGE_PREFERENCE_COOKIE_NAME: 'openedx-language-preference', - LEARNING_BASE_URL: 'http://localhost:2000', LMS_BASE_URL: 'http://localhost:18000', + LEARNING_BASE_URL: 'http://localhost:2000', LOGIN_URL: 'http://localhost:18000/login', LOGOUT_URL: 'http://localhost:18000/logout', STUDIO_BASE_URL: 'http://localhost:18010', MARKETING_SITE_BASE_URL: 'http://localhost:18000', - ORDER_HISTORY_URL: 'http://localhost:1996/orders', - REFRESH_ACCESS_TOKEN_ENDPOINT: 'http://localhost:18000/login_refresh', - SEGMENT_KEY: null, - SITE_NAME: 'localhost', + ORDER_HISTORY_URL: 'localhost:1996/orders', + REFRESH_ACCESS_TOKEN_API_PATH: '/login_refresh', + SEGMENT_KEY: 'segment_whoa', + SITE_NAME: 'edX', USER_INFO_COOKIE_NAME: 'edx-user-info', LOGO_URL: 'https://edx-cdn.org/v3/default/logo.svg', LOGO_TRADEMARK_URL: 'https://edx-cdn.org/v3/default/logo-trademark.svg', LOGO_WHITE_URL: 'https://edx-cdn.org/v3/default/logo-white.svg', FAVICON_URL: 'https://edx-cdn.org/v3/default/favicon.ico', - IGNORED_ERROR_REGEX: null, - MFE_CONFIG_API_URL: null, - APP_ID: null, + APP_ID: 'runtime', SUPPORT_URL: 'https://support.edx.org', + ENVIRONMENT: 'test', + IGNORED_ERROR_REGEX: null, + PUBLIC_PATH: '/' }; + +export default config; diff --git a/runtime/testing/initializeMockApp.js b/runtime/testing/initializeMockApp.js index 8e2bead1..30e70b5c 100644 --- a/runtime/testing/initializeMockApp.js +++ b/runtime/testing/initializeMockApp.js @@ -1,6 +1,8 @@ +import siteConfig from 'site.config'; + import { configure as configureAnalytics, MockAnalyticsService } from '../analytics'; -import { configure as configureAuth, MockAuthService } from '../auth'; -import { getConfig } from '../config'; +import { configure as configureAuth, MockAuthService, setAuthenticatedUser } from '../auth'; +import { getConfig, mergeConfig } from '../config'; import { configure as configureI18n } from '../i18n'; import { configure as configureLogging, MockLoggingService } from '../logging'; import mockMessages from './mockMessages'; @@ -46,15 +48,20 @@ export default function initializeMockApp({ messages = mockMessages, authenticatedUser = null, } = {}) { + const config = siteConfig; + mergeConfig(config); + const loggingService = configureLogging(MockLoggingService, { config: getConfig(), }); const authService = configureAuth(MockAuthService, { - config: { ...getConfig(), authenticatedUser }, + config: getConfig(), loggingService, }); + setAuthenticatedUser(authenticatedUser); + const analyticsService = configureAnalytics(MockAnalyticsService, { config: getConfig(), httpClient: authService.getAuthenticatedHttpClient(), diff --git a/runtime/utils.js b/runtime/utils.js index a0353f08..8083f1ba 100644 --- a/runtime/utils.js +++ b/runtime/utils.js @@ -180,24 +180,3 @@ export function getQueryParameters(search = global.location.search) { return Object.assign(params, { [key]: decodeURIComponent(value) }); }, {}); } - -/** - * This function helps catch a certain class of misconfiguration in which configuration variables - * are not properly defined and/or supplied to a consumer that requires them. Any key that exists - * is still set to "undefined" indicates a misconfiguration further up in the application, and - * should be flagged as an error, and is logged to 'warn'. - * - * Keys that are intended to be falsy should be defined using null, 0, false, etc. - * - * @param {Object} object - * @param {string} requester A human-readable identifier for the code which called this function. - * Used when throwing errors to aid in debugging. - */ -export function ensureDefinedConfig(object, requester) { - Object.keys(object).forEach((key) => { - if (object[key] === undefined) { - // eslint-disable-next-line no-console - console.warn(`Module configuration error: ${key} is required by ${requester}.`); - } - }); -} diff --git a/shell/FederatedComponent.tsx b/shell/FederatedComponent.tsx new file mode 100644 index 00000000..5b9ac897 --- /dev/null +++ b/shell/FederatedComponent.tsx @@ -0,0 +1,44 @@ +import { loadRemote } from '@module-federation/runtime'; +import { + ComponentType, Suspense, useEffect, useState +} from 'react'; +import { FederatedAppConfig } from '../types'; + +function useDynamicImport({ module, scope }) { + const [component, setComponent] = useState<(() => ComponentType) | null>(null); + + useEffect(() => { + if (!module || !scope) { return; } + + const loadComponent = async () => { + try { + const loadedRemote = await loadRemote<{ default: ComponentType }>(`${scope}/${module}`); + if (loadedRemote !== null) { + const { default: Component } = loadedRemote; + setComponent(() => Component); + } else { + // TODO: There was no remote. Throw? + } + } catch (error) { + console.error(`Error loading remote module ${scope}/${module}:`, error); + } + }; + + loadComponent(); + }, [module, scope]); + + return component; +} + +export default function FederatedComponent({ federatedApp }: { federatedApp: FederatedAppConfig }) { + const Component = useDynamicImport({ scope: federatedApp.appId, module: federatedApp.moduleId }); + + return ( + + {Component + // @ts-ignore + ? + : null} + + ); +} diff --git a/shell/__mocks__/env.config.js b/shell/__mocks__/env.config.js deleted file mode 100644 index f053ebf7..00000000 --- a/shell/__mocks__/env.config.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; diff --git a/shell/bootstrap.tsx b/shell/bootstrap.tsx deleted file mode 100644 index dca11c71..00000000 --- a/shell/bootstrap.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { createElement } from 'react'; -import ReactDOM from 'react-dom'; - -import { - APP_INIT_ERROR, APP_READY, - AppProvider, - getConfig, - initialize, - subscribe, -} from '../runtime'; - -import Footer from './footer'; -import Header from './header'; - -const messages = []; - -subscribe(APP_READY, () => { - ReactDOM.render( - -
- {getConfig().app ? createElement(getConfig().app, {}) : null} -