diff --git a/.gitignore b/.gitignore index f110ba720c..026110f222 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,6 @@ npm-debug.log /build/ -/src/environments/environment.ts -/src/environments/environment.dev.ts -/src/environments/environment.prod.ts - /coverage /dist/ diff --git a/README.md b/README.md index 91ad0f9fab..065d313005 100644 --- a/README.md +++ b/README.md @@ -102,48 +102,87 @@ Installing ### Configuring -Default configuration file is located in `src/environments/` folder. +Default configuration file is located in `config/` folder. -To change the default configuration values, create local files that override the parameters you need to change. You can use `environment.template.ts` as a starting point. +To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point. -- Create a new `environment.dev.ts` file in `src/environments/` for a `development` environment; -- Create a new `environment.prod.ts` file in `src/environments/` for a `production` environment; +- Create a new `config.(dev or development).yml` file in `config/` for a `development` environment; +- Create a new `config.(prod or production).yml` file in `config/` for a `production` environment; -The server settings can also be overwritten using an environment file. +The settings can also be overwritten using an environment file or environment variables. This file should be called `.env` and be placed in the project root. -The following settings can be overwritten in this file: +The following non-convention settings: ```bash DSPACE_HOST # The host name of the angular application DSPACE_PORT # The port number of the angular application DSPACE_NAMESPACE # The namespace of the angular application DSPACE_SSL # Whether the angular application uses SSL [true/false] +``` -DSPACE_REST_HOST # The host name of the REST application -DSPACE_REST_PORT # The port number of the REST application -DSPACE_REST_NAMESPACE # The namespace of the REST application -DSPACE_REST_SSL # Whether the angular REST uses SSL [true/false] +All other settings can be set using the following convention for naming the environment variables: + +1. replace all `.` with `_` +2. convert all characters to upper case +3. prefix with `DSPACE_` + +e.g. + +```bash +# The host name of the REST application +dspace.rest.host => DSPACE_REST_HOST + +# The port number of the REST application +dspace.rest.port => DSPACE_REST_PORT + +# The namespace of the REST application +dspace.rest.nameSpace => DSPACE_REST_NAMESPACE + +# Whether the angular REST uses SSL [true/false] +dspace.rest.ssl => DSPACE_REST_SSL + +cache.msToLive.default => CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => AUTH_UI_TIMEUNTILIDLE +``` + +The equavelant to the non-conventional legacy settings: + +```bash +DSPACE_UI_HOST => DSPACE_HOST +DSPACE_UI_PORT => DSPACE_PORT +DSPACE_UI_NAMESPACE => DSPACE_NAMESPACE +DSPACE_UI_SSL => DSPACE_SSL ``` The same settings can also be overwritten by setting system environment variables instead, E.g.: ```bash export DSPACE_HOST=api7.dspace.org +export DSPACE_UI_PORT=4200 ``` -The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides **`environment.(prod, dev or test).ts`** overrides **`environment.common.ts`** +The priority works as follows: **environment variable** overrides **variable in `.env` file** overrides external config set by `DSPACE_APP_CONFIG_PATH` overrides **`config.(prod or dev).yml`** -These configuration sources are collected **at build time**, and written to `src/environments/environment.ts`. At runtime the configuration is fixed, and neither `.env` nor the process' environment will be consulted. +These configuration sources are collected **at run time**, and written to `dist/browser/assets/config.json` for production and `src/app/assets/config.json` for development. + +The configuration file can be externalized by using environment variable `DSPACE_APP_CONFIG_PATH`. #### Using environment variables in code To use environment variables in a UI component, use: ```typescript -import { environment } from '../environment.ts'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; +... +constructor(@Inject(APP_CONFIG) private appConfig: AppConfig) {} +... ``` -This file is generated by the script located in `scripts/set-env.ts`. This script will run automatically before every build, or can be manually triggered using the appropriate `config` script in `package.json` +or + +```typescript +import { environment } from '../environment.ts'; +``` Running the app @@ -231,7 +270,7 @@ E2E tests (aka integration tests) use [Cypress.io](https://www.cypress.io/). Con The test files can be found in the `./cypress/integration/` folder. Before you can run e2e tests, two things are required: -1. You MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `environment.prod.ts` or `environment.common.ts`. You may override this using env variables, see [Configuring](#configuring). +1. You MUST have a running backend (i.e. REST API). By default, the e2e tests look for this at http://localhost:8080/server/ or whatever `rest` backend is defined in your `config.prod.yml` or `config.yml`. You may override this using env variables, see [Configuring](#configuring). 2. Your backend MUST include our Entities Test Data set. Some tests run against a (currently hardcoded) Community/Collection/Item UUID. These UUIDs are all valid for our Entities Test Data set. The Entities Test Data set may be installed easily via Docker, see https://github.com/DSpace/DSpace/tree/main/dspace/src/main/docker-compose#ingest-option-2-ingest-entities-test-data Run `ng e2e` to kick off the tests. This will start Cypress and allow you to select the browser you wish to use, as well as whether you wish to run all tests or an individual test file. Once you click run on test(s), this opens the [Cypress Test Runner](https://docs.cypress.io/guides/core-concepts/test-runner) to run your test(s) and show you the results. @@ -311,49 +350,87 @@ File Structure ``` dspace-angular -├── README.md * This document -├── app.yaml * Application manifest file -├── config * Folder for configuration files -│   ├── environment.default.js * Default configuration files -│   └── environment.test.js * Test configuration files -├── docs * Folder for documentation +├── config * +│ └── config.yml * Default app config ├── cypress * Folder for Cypress (https://cypress.io/) / e2e tests -│   ├── integration * Folder for e2e/integration test files -│   ├── fixtures * Folder for any fixtures needed by e2e tests -│   ├── plugins * Folder for Cypress plugins (if any) -│   ├── support * Folder for global e2e test actions/commands (run for all tests) -│   └── tsconfig.json * TypeScript configuration file for e2e tests +│ ├── downloads * +│ ├── fixtures * Folder for e2e/integration test files +│ ├── integration * Folder for any fixtures needed by e2e tests +│ ├── plugins * Folder for Cypress plugins (if any) +│ ├── support * Folder for global e2e test actions/commands (run for all tests) +│ └── tsconfig.json * TypeScript configuration file for e2e tests +├── docker * +│ ├── cli.assetstore.yml * +│ ├── cli.ingest.yml * +│ ├── cli.yml * +│ ├── db.entities.yml * +│ ├── docker-compose-ci.yml * +│ ├── docker-compose-rest.yml * +│ ├── docker-compose.yml * +│ ├── environment.dev.ts * +│ ├── local.cfg * +│ └── README.md * +├── docs * Folder for documentation +│ └── Configuration.md * Configuration documentation +├── scripts * +│ ├── merge-i18n-files.ts * +│ ├── serve.ts * +│ ├── sync-i18n-files.ts * +│ ├── test-rest.ts * +│ └── webpack.js * +├── src * The source of the application +│ ├── app * The source code of the application, subdivided by module/page. +│ ├── assets * Folder for static resources +│ │ ├── fonts * Folder for fonts +│ │ ├── i18n * Folder for i18n translations +│ │ └── images * Folder for images +│ ├── backend * Folder containing a mock of the REST API, hosted by the express server +│ ├── config * +│ ├── environments * +│ │ ├── environment.production.ts * Production configuration files +│ │ ├── environment.test.ts * Test configuration files +│ │ └── environment.ts * Default (development) configuration files +│ ├── mirador-viewer * +│ ├── modules * +│ ├── ngx-translate-loaders * +│ ├── styles * Folder containing global styles +│ ├── themes * Folder containing available themes +│ │ ├── custom * Template folder for creating a custom theme +│ │ └── dspace * Default 'dspace' theme +│ ├── index.csr.html * The index file for client side rendering fallback +│ ├── index.html * The index file +│ ├── main.browser.ts * The bootstrap file for the client +│ ├── main.server.ts * The express (http://expressjs.com/) config and bootstrap file for the server +│ ├── polyfills.ts * +│ ├── robots.txt * The robots.txt file +│ ├── test.ts * +│ └── typings.d.ts * +├── webpack * +│ ├── helpers.ts * Webpack helpers +│ ├── webpack.browser.ts * Webpack (https://webpack.github.io/) config for browser build +│ ├── webpack.common.ts * Webpack (https://webpack.github.io/) common build config +│ ├── webpack.mirador.config.ts * Webpack (https://webpack.github.io/) config for mirador config build +│ ├── webpack.prod.ts * Webpack (https://webpack.github.io/) config for prod build +│ └── webpack.test.ts * Webpack (https://webpack.github.io/) config for test build +├── angular.json * Angular CLI (https://angular.io/cli) configuration +├── cypress.json * Cypress Test (https://www.cypress.io/) configuration +├── Dockerfile * ├── karma.conf.js * Karma configuration file for Unit Test +├── LICENSE * +├── LICENSES_THIRD_PARTY * ├── nodemon.json * Nodemon (https://nodemon.io/) configuration ├── package.json * This file describes the npm package for this project, its dependencies, scripts, etc. -├── postcss.config.js * PostCSS (http://postcss.org/) configuration file -├── src * The source of the application -│   ├── app * The source code of the application, subdivided by module/page. -│   ├── assets * Folder for static resources -│   │   ├── fonts * Folder for fonts -│   │   ├── i18n * Folder for i18n translations -│   | └── en.json5 * i18n translations for English -│   │   └── images * Folder for images -│   ├── backend * Folder containing a mock of the REST API, hosted by the express server -│   ├── config * -│   ├── index.csr.html * The index file for client side rendering fallback -│   ├── index.html * The index file -│   ├── main.browser.ts * The bootstrap file for the client -│   ├── main.server.ts * The express (http://expressjs.com/) config and bootstrap file for the server -│   ├── robots.txt * The robots.txt file -│   ├── modules * -│   ├── styles * Folder containing global styles -│   └── themes * Folder containing available themes -│      ├── custom * Template folder for creating a custom theme -│      └── dspace * Default 'dspace' theme -├── tsconfig.json * TypeScript config +├── postcss.config.js * PostCSS (http://postcss.org/) configuration +├── README.md * This document +├── SECURITY.md * +├── server.ts * Angular Universal Node.js Express server +├── tsconfig.app.json * TypeScript config for browser (app) +├── tsconfig.json * TypeScript common config +├── tsconfig.server.json * TypeScript config for server +├── tsconfig.spec.json * TypeScript config for tests +├── tsconfig.ts-node.json * TypeScript config for using ts-node directly ├── tslint.json * TSLint (https://palantir.github.io/tslint/) configuration ├── typedoc.json * TYPEDOC configuration -├── webpack * Webpack (https://webpack.github.io/) config directory -│   ├── webpack.browser.ts * Webpack (https://webpack.github.io/) config for client build -│   ├── webpack.common.ts * -│   ├── webpack.prod.ts * Webpack (https://webpack.github.io/) config for production build -│   └── webpack.test.ts * Webpack (https://webpack.github.io/) config for test build └── yarn.lock * Yarn lockfile (https://yarnpkg.com/en/docs/yarn-lock) ``` diff --git a/angular.json b/angular.json index c6607fc80a..a0a4cd8ea1 100644 --- a/angular.json +++ b/angular.json @@ -68,6 +68,12 @@ }, "configurations": { "production": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.production.ts" + } + ], "optimization": true, "outputHashing": "all", "extractCss": true, @@ -139,6 +145,16 @@ } ], "scripts": [] + }, + "configurations": { + "test": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.test.ts" + } + ] + } } }, "lint": { @@ -183,7 +199,13 @@ "configurations": { "production": { "sourceMap": false, - "optimization": true + "optimization": true, + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.production.ts" + } + ] } } }, diff --git a/config/.gitignore b/config/.gitignore new file mode 100644 index 0000000000..a420ca4302 --- /dev/null +++ b/config/.gitignore @@ -0,0 +1,2 @@ +config.*.yml +!config.example.yml diff --git a/config/config.example.yml b/config/config.example.yml new file mode 100644 index 0000000000..1e035889a5 --- /dev/null +++ b/config/config.example.yml @@ -0,0 +1,233 @@ +# NOTE: will log all redux actions and transfers in console +debug: false + +# Angular Universal server settings +# NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. +ui: + ssl: false + host: localhost + port: 4000 + # NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: / + # The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: + windowMs: 60000 # 1 minute + max: 500 # limit each IP to 500 requests per windowMs + +# The REST API server settings +# NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. +rest: + ssl: true + host: api7.dspace.org + port: 443 + # NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: /server + +# Caching settings +cache: + # NOTE: how long should objects be cached for by default + msToLive: + default: 900000 # 15 minutes + control: max-age=60 # revalidate browser + autoSync: + defaultTime: 0 + maxBufferSize: 100 + timePerMethod: + PATCH: 3 # time in seconds + +# Authentication settings +auth: + # Authentication UI settings + ui: + # the amount of time before the idle warning is shown + timeUntilIdle: 900000 # 15 minutes + # the amount of time the user has to react after the idle warning is shown before they are logged out. + idleGracePeriod: 300000 # 5 minutes + # Authentication REST settings + rest: + # If the rest token expires in less than this amount of time, it will be refreshed automatically. + # This is independent from the idle warning. + timeLeftBeforeTokenRefresh: 120000 # 2 minutes + +# Form settings +form: + # NOTE: Map server-side validators to comparative Angular form validators + validatorMap: + required: required + regex: pattern + +# Notification settings +notifications: + rtl: false + position: + - top + - right + maxStack: 8 + # NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically + timeOut: 5000 # 5 second + clickToClose: true + # NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' + animate: scale + +# Submission settings +submission: + autosave: + # NOTE: which metadata trigger an autosave + metadata: [] + # NOTE: after how many time (milliseconds) submission is saved automatically + # eg. timer: 5 * (1000 * 60); // 5 minutes + timer: 0 + icons: + metadata: + # NOTE: example of configuration + # # NOTE: metadata name + # - name: dc.author + # # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + # style: fas fa-user + - name: dc.author + style: fas fa-user + # default configuration + - name: default + style: '' + authority: + confidence: + # NOTE: example of configuration + # # NOTE: confidence value + # - name: dc.author + # # NOTE: fontawesome (v5.x) icon classes and bootstrap utility classes can be used + # style: fa-user + - value: 600 + style: text-success + - value: 500 + style: text-info + - value: 400 + style: text-warning + # default configuration + - value: default + style: text-muted + +# Default Language in which the UI will be rendered if the user's browser language is not an active language +defaultLanguage: en + +# Languages. DSpace Angular holds a message catalog for each of the following languages. +# When set to active, users will be able to switch to the use of this language in the user interface. +languages: + - code: en + label: English + active: true + - code: cs + label: Čeština + active: true + - code: de + label: Deutsch + active: true + - code: es + label: Español + active: true + - code: fr + label: Français + active: true + - code: lv + label: Latviešu + active: true + - code: hu + label: Magyar + active: true + - code: nl + label: Nederlands + active: true + - code: pt-PT + label: Português + active: true + - code: pt-BR + label: Português do Brasil + active: true + - code: fi + label: Suomi + active: true + +# Browse-By Pages +browseBy: + # Amount of years to display using jumps of one year (current year - oneYearLimit) + oneYearLimit: 10 + # Limit for years to display using jumps of five years (current year - fiveYearLimit) + fiveYearLimit: 30 + # The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) + defaultLowerLimit: 1900 + +# Item Page Config +item: + edit: + undoTimeout: 10000 # 10 seconds + +# Collection Page Config +collection: + edit: + undoTimeout: 10000 # 10 seconds + +# Theme Config +themes: + # Add additional themes here. In the case where multiple themes match a route, the first one + # in this list will get priority. It is advisable to always have a theme that matches + # every route as the last one + # + # # A theme with a handle property will match the community, collection or item with the given + # # handle, and all collections and/or items within it + # - name: 'custom', + # handle: '10673/1233' + # + # # A theme with a regex property will match the route using a regular expression. If it + # # matches the route for a community or collection it will also apply to all collections + # # and/or items within it + # - name: 'custom', + # regex: 'collections\/e8043bc2.*' + # + # # A theme with a uuid property will match the community, collection or item with the given + # # ID, and all collections and/or items within it + # - name: 'custom', + # uuid: '0958c910-2037-42a9-81c7-dca80e3892b4' + # + # # The extends property specifies an ancestor theme (by name). Whenever a themed component is not found + # # in the current theme, its ancestor theme(s) will be checked recursively before falling back to default. + # - name: 'custom-A', + # extends: 'custom-B', + # # Any of the matching properties above can be used + # handle: '10673/34' + # + # - name: 'custom-B', + # extends: 'custom', + # handle: '10673/12' + # + # # A theme with only a name will match every route + # name: 'custom' + # + # # This theme will use the default bootstrap styling for DSpace components + # - name: BASE_THEME_NAME + # + - name: dspace + headTags: + - tagName: link + attributes: + rel: icon + href: assets/dspace/images/favicons/favicon.ico + sizes: any + - tagName: link + attributes: + rel: icon + href: assets/dspace/images/favicons/favicon.svg + type: image/svg+xml + - tagName: link + attributes: + rel: apple-touch-icon + href: assets/dspace/images/favicons/apple-touch-icon.png + - tagName: link + attributes: + rel: manifest + href: assets/dspace/images/favicons/manifest.webmanifest + +# Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with 'image' or 'video'). +# For images, this enables a gallery viewer where you can zoom or page through images. +# For videos, this enables embedded video streaming +mediaViewer: + image: false + video: false diff --git a/config/config.yml b/config/config.yml new file mode 100644 index 0000000000..b5eecd112f --- /dev/null +++ b/config/config.yml @@ -0,0 +1,5 @@ +rest: + ssl: true + host: api7.dspace.org + port: 443 + nameSpace: /server diff --git a/cypress/.gitignore b/cypress/.gitignore new file mode 100644 index 0000000000..99bd2a6312 --- /dev/null +++ b/cypress/.gitignore @@ -0,0 +1,2 @@ +screenshots/ +videos/ diff --git a/docs/Configuration.md b/docs/Configuration.md index f4fff1166c..62fa444cc0 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -1,26 +1,30 @@ # Configuration -Default configuration file is located in `src/environments/` folder. All configuration options should be listed in the default configuration file `src/environments/environment.common.ts`. Please do not change this file directly! To change the default configuration values, create local files that override the parameters you need to change. You can use `environment.template.ts` as a starting point. +Default configuration file is located at `config/config.yml`. All configuration options should be listed in the default typescript file `src/config/default-app-config.ts`. Please do not change this file directly! To override the default configuration values, create local files that override the parameters you need to change. You can use `config.example.yml` as a starting point. -- Create a new `environment.dev.ts` file in `src/environments/` for `development` environment; -- Create a new `environment.prod.ts` file in `src/environments/` for `production` environment; +- Create a new `config.(dev or development).yml` file in `config/` for `development` environment; +- Create a new `config.(prod or production).yml` file in `config/` for `production` environment; -Some few configuration options can be overridden by setting environment variables. These and the variable names are listed below. +Alternatively, create a desired app config file at an external location and set the path as environment variable `DSPACE_APP_CONFIG_PATH`. + +e.g. +``` +DSPACE_APP_CONFIG_PATH=/usr/local/dspace/config/config.yml +``` + +Configuration options can be overridden by setting environment variables. ## Nodejs server When you start dspace-angular on node, it spins up an http server on which it listens for incoming connections. You can define the ip address and port the server should bind itsself to, and if ssl should be enabled not. By default it listens on `localhost:4000`. If you want it to listen on all your network connections, configure it to bind itself to `0.0.0.0`. To change this configuration, change the options `ui.host`, `ui.port` and `ui.ssl` in the appropriate configuration file (see above): -``` -export const environment = { - // Angular UI settings. - ui: { - ssl: false, - host: 'localhost', - port: 4000, - nameSpace: '/' - } -}; + +```yaml +ui: + ssl: false + host: localhost + port: 4000 + nameSpace: / ``` Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: @@ -30,21 +34,24 @@ Alternately you can set the following environment variables. If any of these are DSPACE_PORT=4000 DSPACE_NAMESPACE=/ ``` +or +``` + DSPACE_UI_SSL=true + DSPACE_UI_HOST=localhost + DSPACE_UI_PORT=4000 + DSPACE_UI_NAMESPACE=/ +``` ## DSpace's REST endpoint dspace-angular connects to your DSpace installation by using its REST endpoint. To do so, you have to define the ip address, port and if ssl should be enabled. You can do this in a configuration file (see above) by adding the following options: -``` -export const environment = { - // The REST API server settings. - rest: { - ssl: true, - host: 'api7.dspace.org', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server' - } -}; +```yaml +rest: + ssl: true + host: api7.dspace.org + port: 443 + nameSpace: /server +} ``` Alternately you can set the following environment variables. If any of these are set, it will override all configuration files: @@ -55,6 +62,21 @@ Alternately you can set the following environment variables. If any of these are DSPACE_REST_NAMESPACE=/server ``` +## Environment variable naming convention + +Settings can be set using the following convention for naming the environment variables: + +1. replace all `.` with `_` +2. convert all characters to upper case +3. prefix with `DSPACE_` + +e.g. + +``` +cache.msToLive.default => DSPACE_CACHE_MSTOLIVE_DEFAULT +auth.ui.timeUntilIdle => DSPACE_AUTH_UI_TIMEUNTILIDLE +``` + ## Supporting analytics services other than Google Analytics This project makes use of [Angulartics](https://angulartics.github.io/angulartics2/) to track usage events and send them to Google Analytics. diff --git a/mock-nodemon.json b/mock-nodemon.json deleted file mode 100644 index 18fc86bd9d..0000000000 --- a/mock-nodemon.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "watch": ["src/environments/mock-environment.ts"], - "ext": "ts", - "exec": "ts-node --project ./tsconfig.ts-node.json scripts/set-mock-env.ts" -} diff --git a/nodemon.json b/nodemon.json index 39e9d9aa5b..dec8d9724c 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,6 @@ { - "watch": ["src/environments"], - "ext": "ts", - "ignore": ["src/environments/environment.ts", "src/environments/mock-environment.ts"], - "exec": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --dev" + "watch": [ + "config" + ], + "ext": "json" } diff --git a/package.json b/package.json index 28ffd3d970..403973ef98 100644 --- a/package.json +++ b/package.json @@ -3,53 +3,40 @@ "version": "0.0.0", "scripts": { "ng": "ng", - "config:dev": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --dev", - "config:prod": "ts-node --project ./tsconfig.ts-node.json scripts/set-env.ts --prod", - "config:test": "ts-node --project ./tsconfig.ts-node.json scripts/set-mock-env.ts", - "config:test:watch": "nodemon --config mock-nodemon.json", - "config:dev:watch": "nodemon", - "config:check:rest": "yarn run config:prod && ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", - "config:dev:check:rest": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", - "prestart:dev": "yarn run config:dev", - "prebuild": "yarn run config:dev", - "pretest": "yarn run config:test", - "pretest:watch": "yarn run config:test", - "pretest:headless": "yarn run config:test", - "prebuild:prod": "yarn run config:prod", - "pree2e": "yarn run config:prod", + "config:watch": "nodemon", + "test:rest": "ts-node --project ./tsconfig.ts-node.json scripts/test-rest.ts", "start": "yarn run start:prod", - "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", - "start:dev": "npm-run-all --parallel config:dev:watch serve", - "start:prod": "yarn run build:prod && yarn run serve:ssr", + "start:dev": "nodemon --exec \"cross-env NODE_ENV=development yarn run serve\"", + "start:prod": "yarn run build:prod && cross-env NODE_ENV=production yarn run serve:ssr", "start:mirador:prod": "yarn run build:mirador && yarn run start:prod", + "serve": "ts-node --project ./tsconfig.ts-node.json scripts/serve.ts", + "serve:ssr": "node dist/server/main", "analyze": "webpack-bundle-analyzer dist/browser/stats.json", "build": "ng build", "build:stats": "ng build --stats-json", "build:prod": "yarn run build:ssr", "build:ssr": "ng build --configuration production && ng run dspace-angular:server:production", - "test:watch": "npm-run-all --parallel config:test:watch test", - "test": "ng test --sourceMap=true --watch=true", - "test:headless": "ng test --watch=false --sourceMap=true --browsers=ChromeHeadless --code-coverage", + "test": "ng test --sourceMap=true --watch=false --configuration test", + "test:watch": "nodemon --exec \"ng test --sourceMap=true --watch=true --configuration test\"", + "test:headless": "ng test --sourceMap=true --watch=false --configuration test --browsers=ChromeHeadless --code-coverage", "lint": "ng lint", "lint-fix": "ng lint --fix=true", "e2e": "ng e2e", - "serve:ssr": "node dist/server/main", + "clean:dev:config": "rimraf src/assets/config.json", "clean:coverage": "rimraf coverage", "clean:dist": "rimraf dist", "clean:doc": "rimraf doc", "clean:log": "rimraf *.log*", "clean:json": "rimraf *.records.json", - "clean:bld": "rimraf build", "clean:node": "rimraf node_modules", - "clean:prod": "yarn run clean:coverage && yarn run clean:doc && yarn run clean:dist && yarn run clean:log && yarn run clean:json && yarn run clean:bld", - "clean": "yarn run clean:prod && yarn run clean:env && yarn run clean:node", - "clean:env": "rimraf src/environments/environment.ts", - "sync-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", + "clean:prod": "yarn run clean:dist && yarn run clean:log && yarn run clean:doc && yarn run clean:coverage && yarn run clean:json", + "clean": "yarn run clean:prod && yarn run clean:node && yarn run clean:dev:config", + "sync-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/sync-i18n-files.ts", "build:mirador": "webpack --config webpack/webpack.mirador.config.ts", - "merge-i18n": "yarn run config:dev && ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", - "postinstall": "ngcc", + "merge-i18n": "ts-node --project ./tsconfig.ts-node.json scripts/merge-i18n-files.ts", "cypress:open": "cypress open", - "cypress:run": "cypress run" + "cypress:run": "cypress run", + "env:yaml": "ts-node --project ./tsconfig.ts-node.json scripts/env-to-yaml.ts" }, "browser": { "fs": false, @@ -74,7 +61,6 @@ "@angular/platform-browser-dynamic": "~11.2.14", "@angular/platform-server": "~11.2.14", "@angular/router": "~11.2.14", - "@angularclass/bootloader": "1.0.1", "@kolkov/ngx-gallery": "^1.2.3", "@ng-bootstrap/ng-bootstrap": "9.1.3", "@ng-dynamic-forms/core": "^13.0.0", @@ -102,9 +88,10 @@ "file-saver": "^2.0.5", "filesize": "^6.1.0", "font-awesome": "4.7.0", - "https": "1.0.0", "http-proxy-middleware": "^1.0.5", + "https": "1.0.0", "js-cookie": "2.2.1", + "js-yaml": "^4.1.0", "json5": "^2.1.3", "jsonschema": "1.4.0", "jwt-decode": "^3.1.2", @@ -157,6 +144,7 @@ "codelyzer": "^6.0.0", "compression-webpack-plugin": "^3.0.1", "copy-webpack-plugin": "^6.4.1", + "cross-env": "^7.0.3", "css-loader": "3.4.0", "cssnano": "^4.1.10", "cypress": "8.6.0", @@ -176,8 +164,7 @@ "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "^1.5.0", "karma-mocha-reporter": "2.2.5", - "nodemon": "^2.0.2", - "npm-run-all": "^4.1.5", + "nodemon": "^2.0.15", "optimize-css-assets-webpack-plugin": "^5.0.4", "postcss-apply": "0.11.0", "postcss-import": "^12.0.1", diff --git a/scripts/env-to-yaml.ts b/scripts/env-to-yaml.ts new file mode 100644 index 0000000000..edcdfd90b4 --- /dev/null +++ b/scripts/env-to-yaml.ts @@ -0,0 +1,39 @@ +import * as fs from 'fs'; +import * as yaml from 'js-yaml'; +import { join } from 'path'; + +/** + * Script to help convert previous version environment.*.ts to yaml. + * + * Usage (see package.json): + * + * yarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file) * + */ + +const args = process.argv.slice(2); +if (args[0] === undefined) { + console.log(`Usage:\n\tyarn env:yaml [relative path to environment.ts file] (optional relative path to write yaml file)\n`); + process.exit(0); +} + +const envFullPath = join(process.cwd(), args[0]); + +if (!fs.existsSync(envFullPath)) { + console.error(`Error:\n${envFullPath} does not exist\n`); + process.exit(1); +} + +try { + const env = require(envFullPath); + + const config = yaml.dump(env); + if (args[1]) { + const ymlFullPath = join(process.cwd(), args[1]); + fs.writeFileSync(ymlFullPath, config); + } else { + console.log(config); + } +} catch (e) { + console.error(e); +} + diff --git a/scripts/serve.ts b/scripts/serve.ts index c69f8e8a21..bf5506b8bd 100644 --- a/scripts/serve.ts +++ b/scripts/serve.ts @@ -1,11 +1,14 @@ -import { environment } from '../src/environments/environment'; - import * as child from 'child_process'; +import { AppConfig } from '../src/config/app-config.interface'; +import { buildAppConfig } from '../src/config/config.server'; + +const appConfig: AppConfig = buildAppConfig(); + /** - * Calls `ng serve` with the following arguments configured for the UI in the environment file: host, port, nameSpace, ssl + * Calls `ng serve` with the following arguments configured for the UI in the app config: host, port, nameSpace, ssl */ child.spawn( - `ng serve --host ${environment.ui.host} --port ${environment.ui.port} --servePath ${environment.ui.nameSpace} --ssl ${environment.ui.ssl}`, - { stdio:'inherit', shell: true } + `ng serve --host ${appConfig.ui.host} --port ${appConfig.ui.port} --serve-path ${appConfig.ui.nameSpace} --ssl ${appConfig.ui.ssl}`, + { stdio: 'inherit', shell: true } ); diff --git a/scripts/set-env.ts b/scripts/set-env.ts deleted file mode 100644 index b3516ae68f..0000000000 --- a/scripts/set-env.ts +++ /dev/null @@ -1,116 +0,0 @@ -import { writeFile } from 'fs'; -import { environment as commonEnv } from '../src/environments/environment.common'; -import { GlobalConfig } from '../src/config/global-config.interface'; -import { ServerConfig } from '../src/config/server-config.interface'; -import { hasValue } from '../src/app/shared/empty.util'; - -// Configure Angular `environment.ts` file path -const targetPath = './src/environments/environment.ts'; -// Load node modules -const colors = require('colors'); -require('dotenv').config(); -const merge = require('deepmerge'); -const mergeOptions = { arrayMerge: (destinationArray, sourceArray, options) => sourceArray }; -const environment = process.argv[2]; -let environmentFilePath; -let production = false; - -switch (environment) { - case '--prod': - case '--production': - production = true; - console.log(`Building ${colors.red.bold(`production`)} environment`); - environmentFilePath = '../src/environments/environment.prod.ts'; - break; - case '--test': - console.log(`Building ${colors.blue.bold(`test`)} environment`); - environmentFilePath = '../src/environments/environment.test.ts'; - break; - default: - console.log(`Building ${colors.green.bold(`development`)} environment`); - environmentFilePath = '../src/environments/environment.dev.ts'; -} - -const processEnv = { - ui: createServerConfig( - process.env.DSPACE_HOST, - process.env.DSPACE_PORT, - process.env.DSPACE_NAMESPACE, - process.env.DSPACE_SSL), - rest: createServerConfig( - process.env.DSPACE_REST_HOST, - process.env.DSPACE_REST_PORT, - process.env.DSPACE_REST_NAMESPACE, - process.env.DSPACE_REST_SSL) -} as GlobalConfig; - -import(environmentFilePath) - .then((file) => generateEnvironmentFile(merge.all([commonEnv, file.environment, processEnv], mergeOptions))) - .catch(() => { - console.log(colors.yellow.bold(`No specific environment file found for ` + environment)); - generateEnvironmentFile(merge(commonEnv, processEnv, mergeOptions)) - }); - -function generateEnvironmentFile(file: GlobalConfig): void { - file.production = production; - buildBaseUrls(file); - const contents = `export const environment = ` + JSON.stringify(file); - writeFile(targetPath, contents, (err) => { - if (err) { - throw console.error(err); - } else { - console.log(`Angular ${colors.bold('environment.ts')} file generated correctly at ${colors.bold(targetPath)} \n`); - } - }); -} - -// allow to override a few important options by environment variables -function createServerConfig(host?: string, port?: string, nameSpace?: string, ssl?: string): ServerConfig { - const result = {} as any; - if (hasValue(host)) { - result.host = host; - } - - if (hasValue(nameSpace)) { - result.nameSpace = nameSpace; - } - - if (hasValue(port)) { - result.port = Number(port); - } - - if (hasValue(ssl)) { - result.ssl = ssl.trim().match(/^(true|1|yes)$/i) ? true : false; - } - - return result; -} - -function buildBaseUrls(config: GlobalConfig): void { - for (const key in config) { - if (config.hasOwnProperty(key) && config[key].host) { - config[key].baseUrl = [ - getProtocol(config[key].ssl), - getHost(config[key].host), - getPort(config[key].port), - getNameSpace(config[key].nameSpace) - ].join(''); - } - } -} - -function getProtocol(ssl: boolean): string { - return ssl ? 'https://' : 'http://'; -} - -function getHost(host: string): string { - return host; -} - -function getPort(port: number): string { - return port ? (port !== 80 && port !== 443) ? ':' + port : '' : ''; -} - -function getNameSpace(nameSpace: string): string { - return nameSpace ? nameSpace.charAt(0) === '/' ? nameSpace : '/' + nameSpace : ''; -} diff --git a/scripts/set-mock-env.ts b/scripts/set-mock-env.ts deleted file mode 100644 index 5271432896..0000000000 --- a/scripts/set-mock-env.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { copyFile } from 'fs'; - -// Configure Angular `environment.ts` file path -const sourcePath = './src/environments/mock-environment.ts'; -const targetPath = './src/environments/environment.ts'; - -// destination.txt will be created or overwritten by default. -copyFile(sourcePath, targetPath, (err) => { - if (err) throw err; - console.log(sourcePath + ' was copied to ' + targetPath); -}); diff --git a/scripts/test-rest.ts b/scripts/test-rest.ts index b12a9929c2..aa3b64f62b 100644 --- a/scripts/test-rest.ts +++ b/scripts/test-rest.ts @@ -1,21 +1,25 @@ import * as http from 'http'; import * as https from 'https'; -import { environment } from '../src/environments/environment'; + +import { AppConfig } from '../src/config/app-config.interface'; +import { buildAppConfig } from '../src/config/config.server'; + +const appConfig: AppConfig = buildAppConfig(); /** - * Script to test the connection with the configured REST API (in the 'rest' settings of your environment.*.ts) + * Script to test the connection with the configured REST API (in the 'rest' settings of your config.*.yaml) * * This script is useful to test for any Node.js connection issues with your REST API. * - * Usage (see package.json): yarn test:rest-api + * Usage (see package.json): yarn test:rest */ // Get root URL of configured REST API -const restUrl = environment.rest.baseUrl + '/api'; +const restUrl = appConfig.rest.baseUrl + '/api'; console.log(`...Testing connection to REST API at ${restUrl}...\n`); // If SSL enabled, test via HTTPS, else via HTTP -if (environment.rest.ssl) { +if (appConfig.rest.ssl) { const req = https.request(restUrl, (res) => { console.log(`RESPONSE: ${res.statusCode} ${res.statusMessage} \n`); res.on('data', (data) => { @@ -55,7 +59,7 @@ function checkJSONResponse(responseData: any): any { console.log(`\t"dspaceVersion" = ${parsedData.dspaceVersion}`); console.log(`\t"dspaceUI" = ${parsedData.dspaceUI}`); console.log(`\t"dspaceServer" = ${parsedData.dspaceServer}`); - console.log(`\t"dspaceServer" property matches UI's "rest" config? ${(parsedData.dspaceServer === environment.rest.baseUrl)}`); + console.log(`\t"dspaceServer" property matches UI's "rest" config? ${(parsedData.dspaceServer === appConfig.rest.baseUrl)}`); // Check for "authn" and "sites" in "_links" section as they should always exist (even if no data)! const linksFound: string[] = Object.keys(parsedData._links); console.log(`\tDoes "/api" endpoint have HAL links ("_links" section)? ${linksFound.includes('authn') && linksFound.includes('sites')}`); diff --git a/server.ts b/server.ts index c00bdb5ef5..da3b877bc1 100644 --- a/server.ts +++ b/server.ts @@ -19,27 +19,34 @@ import 'zone.js/dist/zone-node'; import 'reflect-metadata'; import 'rxjs'; -import * as fs from 'fs'; import * as pem from 'pem'; import * as https from 'https'; import * as morgan from 'morgan'; import * as express from 'express'; import * as bodyParser from 'body-parser'; import * as compression from 'compression'; + +import { existsSync, readFileSync } from 'fs'; import { join } from 'path'; +import { APP_BASE_HREF } from '@angular/common'; import { enableProdMode } from '@angular/core'; -import { existsSync } from 'fs'; + import { ngExpressEngine } from '@nguniversal/express-engine'; import { REQUEST, RESPONSE } from '@nguniversal/express-engine/tokens'; + import { environment } from './src/environments/environment'; import { createProxyMiddleware } from 'http-proxy-middleware'; import { hasValue, hasNoValue } from './src/app/shared/empty.util'; -import { APP_BASE_HREF } from '@angular/common'; + import { UIServerConfig } from './src/config/ui-server-config.interface'; import { ServerAppModule } from './src/main.server'; +import { buildAppConfig } from './src/config/config.server'; +import { AppConfig, APP_CONFIG } from './src/config/app-config.interface'; +import { extendEnvironmentWithAppConfig } from './src/config/config.util'; + /* * Set path for the browser application's dist folder */ @@ -51,6 +58,11 @@ const indexHtml = existsSync(join(DIST_FOLDER, 'index.html')) ? 'index.html' : ' const cookieParser = require('cookie-parser'); +const appConfig: AppConfig = buildAppConfig(join(DIST_FOLDER, 'assets/config.json')); + +// extend environment with app config for server +extendEnvironmentWithAppConfig(environment, appConfig); + // The Express app is exported so that it can be used by serverless Functions. export function app() { @@ -100,7 +112,11 @@ export function app() { provide: RESPONSE, useValue: (options as any).req.res, }, - ], + { + provide: APP_CONFIG, + useValue: environment + } + ] })(_, (options as any), callback) ); @@ -237,14 +253,14 @@ function start() { if (environment.ui.ssl) { let serviceKey; try { - serviceKey = fs.readFileSync('./config/ssl/key.pem'); + serviceKey = readFileSync('./config/ssl/key.pem'); } catch (e) { console.warn('Service key not found at ./config/ssl/key.pem'); } let certificate; try { - certificate = fs.readFileSync('./config/ssl/cert.pem'); + certificate = readFileSync('./config/ssl/cert.pem'); } catch (e) { console.warn('Certificate not found at ./config/ssl/key.pem'); } diff --git a/src/app/app.component.spec.ts b/src/app/app.component.spec.ts index 937b71eb5a..a892e34a5a 100644 --- a/src/app/app.component.spec.ts +++ b/src/app/app.component.spec.ts @@ -36,6 +36,8 @@ import { GoogleAnalyticsService } from './statistics/google-analytics.service'; import { ThemeService } from './shared/theme-support/theme.service'; import { getMockThemeService } from './shared/mocks/theme-service.mock'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; +import { APP_CONFIG } from '../config/app-config.interface'; +import { environment } from '../environments/environment'; let comp: AppComponent; let fixture: ComponentFixture; @@ -83,6 +85,7 @@ describe('App component', () => { { provide: LocaleService, useValue: getMockLocaleService() }, { provide: ThemeService, useValue: getMockThemeService() }, { provide: BreadcrumbsService, useValue: breadcrumbsServiceSpy }, + { provide: APP_CONFIG, useValue: environment }, provideMockStore({ initialState }), AppComponent, RouteService diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 03075f4f18..669411d9aa 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,4 +1,5 @@ import { distinctUntilChanged, filter, switchMap, take, withLatestFrom } from 'rxjs/operators'; +import { DOCUMENT, isPlatformBrowser } from '@angular/common'; import { AfterViewInit, ChangeDetectionStrategy, @@ -17,8 +18,10 @@ import { Router, } from '@angular/router'; +import { isEqual } from 'lodash'; import { BehaviorSubject, Observable, of } from 'rxjs'; import { select, Store } from '@ngrx/store'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { TranslateService } from '@ngx-translate/core'; import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; @@ -39,13 +42,12 @@ import { LocaleService } from './core/locale/locale.service'; import { hasNoValue, hasValue, isNotEmpty } from './shared/empty.util'; import { KlaroService } from './shared/cookies/klaro.service'; import { GoogleAnalyticsService } from './statistics/google-analytics.service'; -import { DOCUMENT, isPlatformBrowser } from '@angular/common'; import { ThemeService } from './shared/theme-support/theme.service'; import { BASE_THEME_NAME } from './shared/theme-support/theme.constants'; -import { DEFAULT_THEME_CONFIG } from './shared/theme-support/theme.effects'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { getDefaultThemeConfig } from '../config/config.util'; +import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface'; @Component({ selector: 'ds-app', @@ -59,7 +61,7 @@ export class AppComponent implements OnInit, AfterViewInit { collapsedSidebarWidth: Observable; totalSidebarWidth: Observable; theme: Observable = of({} as any); - notificationOptions = environment.notifications; + notificationOptions; models; /** @@ -88,6 +90,7 @@ export class AppComponent implements OnInit, AfterViewInit { @Inject(NativeWindowService) private _window: NativeWindowRef, @Inject(DOCUMENT) private document: any, @Inject(PLATFORM_ID) private platformId: any, + @Inject(APP_CONFIG) private appConfig: AppConfig, private themeService: ThemeService, private translate: TranslateService, private store: Store, @@ -106,6 +109,12 @@ export class AppComponent implements OnInit, AfterViewInit { @Optional() private googleAnalyticsService: GoogleAnalyticsService, ) { + if (!isEqual(environment, this.appConfig)) { + throw new Error('environment does not match app config!'); + } + + this.notificationOptions = environment.notifications; + /* Use models object so all decorators are actually called */ this.models = models; @@ -116,10 +125,13 @@ export class AppComponent implements OnInit, AfterViewInit { } if (hasValue(themeName)) { this.loadGlobalThemeConfig(themeName); - } else if (hasValue(DEFAULT_THEME_CONFIG)) { - this.loadGlobalThemeConfig(DEFAULT_THEME_CONFIG.name); } else { - this.loadGlobalThemeConfig(BASE_THEME_NAME); + const defaultThemeConfig = getDefaultThemeConfig(); + if (hasValue(defaultThemeConfig)) { + this.loadGlobalThemeConfig(defaultThemeConfig.name); + } else { + this.loadGlobalThemeConfig(BASE_THEME_NAME); + } } }); @@ -305,8 +317,8 @@ export class AppComponent implements OnInit, AfterViewInit { // inherit the head tags of the parent theme return this.createHeadTags(parentThemeName); } - - const defaultThemeName = DEFAULT_THEME_CONFIG.name; + const defaultThemeConfig = getDefaultThemeConfig(); + const defaultThemeName = defaultThemeConfig.name; if ( hasNoValue(defaultThemeName) || themeName === defaultThemeName || @@ -326,7 +338,7 @@ export class AppComponent implements OnInit, AfterViewInit { } // inherit the head tags of the default theme - return this.createHeadTags(DEFAULT_THEME_CONFIG.name); + return this.createHeadTags(defaultThemeConfig.name); } return headTagConfigs.map(this.createHeadTag.bind(this)); diff --git a/src/app/app.module.ts b/src/app/app.module.ts index e2cb10691b..98b2d9fe92 100755 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -1,6 +1,8 @@ import { APP_BASE_HREF, CommonModule } from '@angular/common'; import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http'; import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { AbstractControl } from '@angular/forms'; +import { BrowserModule } from '@angular/platform-browser'; import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; import { EffectsModule } from '@ngrx/effects'; @@ -37,7 +39,6 @@ import { NotificationsBoardComponent } from './shared/notifications/notification import { SharedModule } from './shared/shared.module'; import { BreadcrumbsComponent } from './breadcrumbs/breadcrumbs.component'; import { environment } from '../environments/environment'; -import { BrowserModule } from '@angular/platform-browser'; import { ForbiddenComponent } from './forbidden/forbidden.component'; import { AuthInterceptor } from './core/auth/auth.interceptor'; import { LocaleInterceptor } from './core/locale/locale.interceptor'; @@ -56,14 +57,19 @@ import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { UUIDService } from './core/shared/uuid.service'; import { CookieService } from './core/services/cookie.service'; -import { AbstractControl } from '@angular/forms'; -export function getBase() { - return environment.ui.nameSpace; +import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; + +export function getConfig() { + return environment; } -export function getMetaReducers(): MetaReducer[] { - return environment.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers; +export function getBase(appConfig: AppConfig) { + return appConfig.ui.nameSpace; +} + +export function getMetaReducers(appConfig: AppConfig): MetaReducer[] { + return appConfig.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers; } /** @@ -98,13 +104,19 @@ IMPORTS.push( ); const PROVIDERS = [ + { + provide: APP_CONFIG, + useFactory: getConfig + }, { provide: APP_BASE_HREF, - useFactory: (getBase) + useFactory: getBase, + deps: [APP_CONFIG] }, { provide: USER_PROVIDED_META_REDUCERS, useFactory: getMetaReducers, + deps: [APP_CONFIG] }, { provide: RouterStateSerializer, @@ -117,7 +129,7 @@ const PROVIDERS = [ useFactory: (store: Store,) => { return () => store.dispatch(new CheckAuthenticationTokenAction()); }, - deps: [ Store ], + deps: [Store], multi: true }, // register AuthInterceptor as HttpInterceptor @@ -146,7 +158,7 @@ const PROVIDERS = [ }, // insert the unique id of the user that is using the application utilizing cookies { - provide: APP_INITIALIZER, + provide: APP_INITIALIZER, useFactory: (cookieService: CookieService, uuidService: UUIDService) => { const correlationId = cookieService.get('CORRELATION-ID'); @@ -156,8 +168,8 @@ const PROVIDERS = [ } return () => true; }, - multi: true, - deps: [ CookieService, UUIDService ] + multi: true, + deps: [CookieService, UUIDService] }, { provide: DYNAMIC_ERROR_MESSAGES_MATCHER, @@ -195,7 +207,7 @@ const EXPORTS = [ @NgModule({ imports: [ - BrowserModule.withServerTransition({ appId: 'serverApp' }), + BrowserModule.withServerTransition({ appId: 'dspace-angular' }), ...IMPORTS ], providers: [ diff --git a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts index 8b241af667..7a9f224311 100644 --- a/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts +++ b/src/app/community-page/edit-community-page/community-authorizations/community-authorizations.component.ts @@ -2,8 +2,8 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { first, map } from 'rxjs/operators'; -import { RemoteData } from 'src/app/core/data/remote-data'; -import { DSpaceObject } from 'src/app/core/shared/dspace-object.model'; +import { RemoteData } from '../../../core/data/remote-data'; +import { DSpaceObject } from '../../../core/shared/dspace-object.model'; @Component({ selector: 'ds-community-authorizations', diff --git a/src/app/core/data/request.models.ts b/src/app/core/data/request.models.ts index 667e4a0434..a29c99d326 100644 --- a/src/app/core/data/request.models.ts +++ b/src/app/core/data/request.models.ts @@ -20,7 +20,7 @@ export enum IdentifierType { } export abstract class RestRequest { - public responseMsToLive = environment.cache.msToLive.default; + public responseMsToLive; public isMultipart = false; constructor( @@ -30,6 +30,7 @@ export abstract class RestRequest { public body?: any, public options?: HttpOptions, ) { + this.responseMsToLive = environment.cache.msToLive.default; } getResponseParser(): GenericConstructor { diff --git a/src/app/core/shared/hal-endpoint.service.spec.ts b/src/app/core/shared/hal-endpoint.service.spec.ts index 8b71954cbe..b29b8f662e 100644 --- a/src/app/core/shared/hal-endpoint.service.spec.ts +++ b/src/app/core/shared/hal-endpoint.service.spec.ts @@ -89,7 +89,7 @@ describe('HALEndpointService', () => { describe('getRootEndpointMap', () => { it('should send a new EndpointMapRequest', () => { (service as any).getRootEndpointMap(); - const expected = new EndpointMapRequest(requestService.generateRequestId(), environment.rest.baseUrl + 'api'); + const expected = new EndpointMapRequest(requestService.generateRequestId(), `${environment.rest.baseUrl}/api`); expect(requestService.send).toHaveBeenCalledWith(expected, true); }); diff --git a/src/app/item-page/simple/item-types/shared/item.component.ts b/src/app/item-page/simple/item-types/shared/item.component.ts index fa7fba8724..71b0b5b678 100644 --- a/src/app/item-page/simple/item-types/shared/item.component.ts +++ b/src/app/item-page/simple/item-types/shared/item.component.ts @@ -36,9 +36,10 @@ export class ItemComponent implements OnInit { */ iiifQuery$: Observable; - mediaViewer = environment.mediaViewer; + mediaViewer; constructor(protected routeService: RouteService) { + this.mediaViewer = environment.mediaViewer; } ngOnInit(): void { diff --git a/src/app/root/root.component.ts b/src/app/root/root.component.ts index 209f17b520..6ba859ef23 100644 --- a/src/app/root/root.component.ts +++ b/src/app/root/root.component.ts @@ -32,7 +32,7 @@ export class RootComponent implements OnInit { collapsedSidebarWidth: Observable; totalSidebarWidth: Observable; theme: Observable = of({} as any); - notificationOptions = environment.notifications; + notificationOptions; models; /** @@ -58,6 +58,7 @@ export class RootComponent implements OnInit { private menuService: MenuService, private windowService: HostWindowService ) { + this.notificationOptions = environment.notifications; } ngOnInit() { diff --git a/src/app/shared/notifications/notification/notification.component.spec.ts b/src/app/shared/notifications/notification/notification.component.spec.ts index 2bded57636..1d19464578 100644 --- a/src/app/shared/notifications/notification/notification.component.spec.ts +++ b/src/app/shared/notifications/notification/notification.component.spec.ts @@ -10,8 +10,6 @@ import { NotificationsService } from '../notifications.service'; import { NotificationType } from '../models/notification-type'; import { notificationsReducer } from '../notifications.reducers'; import { NotificationOptions } from '../models/notification-options.model'; -import { INotificationBoardOptions } from '../../../../config/notifications-config.interfaces'; -import { GlobalConfig } from '../../../../config/global-config.interface'; import { Notification } from '../models/notification.model'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../../mocks/translate-loader.mock'; @@ -33,16 +31,6 @@ describe('NotificationComponent', () => { /* tslint:disable:no-empty */ notifications: [] }); - const envConfig: GlobalConfig = { - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - timeOut: 5000, - clickToClose: true, - animate: 'scale' - } as INotificationBoardOptions, - } as any; TestBed.configureTestingModule({ imports: [ diff --git a/src/app/shared/notifications/notifications.service.spec.ts b/src/app/shared/notifications/notifications.service.spec.ts index bb0f94eede..92c74e0017 100644 --- a/src/app/shared/notifications/notifications.service.spec.ts +++ b/src/app/shared/notifications/notifications.service.spec.ts @@ -8,7 +8,6 @@ import { of as observableOf } from 'rxjs'; import { NewNotificationAction, RemoveAllNotificationsAction, RemoveNotificationAction } from './notifications.actions'; import { Notification } from './models/notification.model'; import { NotificationType } from './models/notification-type'; -import { GlobalConfig } from '../../../config/global-config.interface'; import { TranslateLoader, TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateLoaderMock } from '../mocks/translate-loader.mock'; import { storeModuleConfig } from '../../app.reducer'; @@ -19,18 +18,6 @@ describe('NotificationsService test', () => { select: observableOf(true) }); let service: NotificationsService; - let envConfig: GlobalConfig; - - envConfig = { - notifications: { - rtl: false, - position: ['top', 'right'], - maxStack: 8, - timeOut: 5000, - clickToClose: true, - animate: 'scale' - }, - } as any; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ diff --git a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts index 7f66eb052c..5cc6397118 100644 --- a/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts +++ b/src/app/shared/resource-policies/form/resource-policy-form.component.spec.ts @@ -32,11 +32,11 @@ import { RESOURCE_POLICY } from '../../../core/resource-policy/models/resource-p import { EPersonMock } from '../../testing/eperson.mock'; import { isNotEmptyOperator } from '../../empty.util'; import { ActivatedRoute, Router } from '@angular/router'; -import { RemoteData } from 'src/app/core/data/remote-data'; +import { RemoteData } from '../../../core/data/remote-data'; import { RouterMock } from '../../mocks/router.mock'; import { Store } from '@ngrx/store'; import { PaginationServiceStub } from '../../testing/pagination-service.stub'; -import { PaginationService } from 'src/app/core/pagination/pagination.service'; +import { PaginationService } from '../../../core/pagination/pagination.service'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { StoreMock } from '../../testing/store.mock'; diff --git a/src/app/shared/theme-support/theme.effects.ts b/src/app/shared/theme-support/theme.effects.ts index e120257728..df74818fa8 100644 --- a/src/app/shared/theme-support/theme.effects.ts +++ b/src/app/shared/theme-support/theme.effects.ts @@ -2,15 +2,9 @@ import { Injectable } from '@angular/core'; import { createEffect, Actions, ofType, ROOT_EFFECTS_INIT } from '@ngrx/effects'; import { map } from 'rxjs/operators'; import { SetThemeAction } from './theme.actions'; -import { environment } from '../../../environments/environment'; -import { hasValue, hasNoValue } from '../empty.util'; +import { hasValue } from '../empty.util'; import { BASE_THEME_NAME } from './theme.constants'; - -export const DEFAULT_THEME_CONFIG = environment.themes.find((themeConfig: any) => - hasNoValue(themeConfig.regex) && - hasNoValue(themeConfig.handle) && - hasNoValue(themeConfig.uuid) -); +import { getDefaultThemeConfig } from '../../../config/config.util'; @Injectable() export class ThemeEffects { @@ -21,8 +15,9 @@ export class ThemeEffects { this.actions$.pipe( ofType(ROOT_EFFECTS_INIT), map(() => { - if (hasValue(DEFAULT_THEME_CONFIG)) { - return new SetThemeAction(DEFAULT_THEME_CONFIG.name); + const defaultThemeConfig = getDefaultThemeConfig(); + if (hasValue(defaultThemeConfig)) { + return new SetThemeAction(defaultThemeConfig.name); } else { return new SetThemeAction(BASE_THEME_NAME); } diff --git a/src/assets/.gitignore b/src/assets/.gitignore new file mode 100644 index 0000000000..d344ba6b06 --- /dev/null +++ b/src/assets/.gitignore @@ -0,0 +1 @@ +config.json diff --git a/src/config/global-config.interface.ts b/src/config/app-config.interface.ts similarity index 80% rename from src/config/global-config.interface.ts rename to src/config/app-config.interface.ts index f8c226bdb1..62d0be7216 100644 --- a/src/config/global-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -1,3 +1,5 @@ +import { InjectionToken } from '@angular/core'; +import { makeStateKey } from '@angular/platform-browser'; import { Config } from './config.interface'; import { ServerConfig } from './server-config.interface'; import { CacheConfig } from './cache-config.interface'; @@ -14,7 +16,7 @@ import { UIServerConfig } from './ui-server-config.interface'; import { MediaViewerConfig } from './media-viewer-config.interface'; import { BrowseByConfig } from './browse-by-config.interface'; -export interface GlobalConfig extends Config { +interface AppConfig extends Config { ui: UIServerConfig; rest: ServerConfig; production: boolean; @@ -33,3 +35,13 @@ export interface GlobalConfig extends Config { themes: ThemeConfig[]; mediaViewer: MediaViewerConfig; } + +const APP_CONFIG = new InjectionToken('APP_CONFIG'); + +const APP_CONFIG_STATE = makeStateKey('APP_CONFIG_STATE'); + +export { + AppConfig, + APP_CONFIG, + APP_CONFIG_STATE +}; diff --git a/src/config/config.server.ts b/src/config/config.server.ts new file mode 100644 index 0000000000..986fc2a092 --- /dev/null +++ b/src/config/config.server.ts @@ -0,0 +1,223 @@ +import * as colors from 'colors'; +import * as fs from 'fs'; +import * as yaml from 'js-yaml'; +import { join } from 'path'; + +import { AppConfig } from './app-config.interface'; +import { Config } from './config.interface'; +import { DefaultAppConfig } from './default-app-config'; +import { ServerConfig } from './server-config.interface'; +import { mergeConfig } from './config.util'; +import { isNotEmpty } from '../app/shared/empty.util'; + +const CONFIG_PATH = join(process.cwd(), 'config'); + +type Environment = 'production' | 'development' | 'test'; + +const DSPACE = (key: string): string => { + return `DSPACE_${key}`; +}; + +const ENV = (key: string, prefix = false): any => { + return prefix ? process.env[DSPACE(key)] : process.env[key]; +}; + +const getBooleanFromString = (variable: string): boolean => { + return variable === 'true' || variable === '1'; +}; + +const getNumberFromString = (variable: string): number => { + return Number(variable); +}; + +const getEnvironment = (): Environment => { + // default to production + let environment: Environment = 'production'; + if (isNotEmpty(ENV('NODE_ENV'))) { + switch (ENV('NODE_ENV')) { + case 'prod': + case 'production': + environment = 'production'; + break; + case 'test': + environment = 'test'; + break; + case 'dev': + case 'development': + environment = 'development'; + break; + default: + console.warn(`Unknown NODE_ENV ${ENV('NODE_ENV')}. Defaulting to production.`); + } + } + + return environment; +}; + +const getLocalConfigPath = (env: Environment) => { + // default to config/config.yml + let localConfigPath = join(CONFIG_PATH, 'config.yml'); + + if (!fs.existsSync(localConfigPath)) { + localConfigPath = join(CONFIG_PATH, 'config.yaml'); + } + + // determine app config filename variations + let envVariations; + switch (env) { + case 'production': + envVariations = ['prod', 'production']; + break; + case 'test': + envVariations = ['test']; + break; + case 'development': + default: + envVariations = ['dev', 'development'] + } + + // check if any environment variations of app config exist + for (const envVariation of envVariations) { + let envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yml`); + if (fs.existsSync(envLocalConfigPath)) { + localConfigPath = envLocalConfigPath; + break; + } else { + envLocalConfigPath = join(CONFIG_PATH, `config.${envVariation}.yaml`); + if (fs.existsSync(envLocalConfigPath)) { + localConfigPath = envLocalConfigPath; + break; + } + } + } + + return localConfigPath; +}; + +const overrideWithConfig = (config: Config, pathToConfig: string) => { + try { + console.log(`Overriding app config with ${pathToConfig}`); + const externalConfig = fs.readFileSync(pathToConfig, 'utf8'); + mergeConfig(config, yaml.load(externalConfig)); + } catch (err) { + console.error(err); + } +}; + +const overrideWithEnvironment = (config: Config, key: string = '') => { + for (const property in config) { + const variable = `${key}${isNotEmpty(key) ? '_' : ''}${property.toUpperCase()}`; + const innerConfig = config[property]; + if (isNotEmpty(innerConfig)) { + if (typeof innerConfig === 'object') { + overrideWithEnvironment(innerConfig, variable); + } else { + const value = ENV(variable, true); + if (isNotEmpty(value)) { + console.log(`Applying environment variable ${DSPACE(variable)} with value ${value}`); + switch (typeof innerConfig) { + case 'number': + config[property] = getNumberFromString(value); + break; + case 'boolean': + config[property] = getBooleanFromString(value); + break; + case 'string': + config[property] = value; + break; + default: + console.warn(`Unsupported environment variable type ${typeof innerConfig} ${DSPACE(variable)}`); + } + } + } + } + } +}; + + + +const buildBaseUrl = (config: ServerConfig): void => { + config.baseUrl = [ + config.ssl ? 'https://' : 'http://', + config.host, + config.port && config.port !== 80 && config.port !== 443 ? `:${config.port}` : '', + config.nameSpace && config.nameSpace.startsWith('/') ? config.nameSpace : `/${config.nameSpace}` + ].join(''); +}; + +/** + * Build app config with the following chain of override. + * + * local config -> environment local config -> external config -> environment variable + * + * Optionally save to file. + * + * @param destConfigPath optional path to save config file + * @returns app config + */ +export const buildAppConfig = (destConfigPath?: string): AppConfig => { + // start with default app config + const appConfig: AppConfig = new DefaultAppConfig(); + + // determine which dist app config by environment + const env = getEnvironment(); + + switch (env) { + case 'production': + console.log(`Building ${colors.red.bold(`production`)} app config`); + break; + case 'test': + console.log(`Building ${colors.blue.bold(`test`)} app config`); + break; + default: + console.log(`Building ${colors.green.bold(`development`)} app config`); + } + + // override with dist config + const localConfigPath = getLocalConfigPath(env); + if (fs.existsSync(localConfigPath)) { + overrideWithConfig(appConfig, localConfigPath); + } else { + console.warn(`Unable to find dist config file at ${localConfigPath}`); + } + + // override with external config if specified by environment variable `DSPACE_APP_CONFIG_PATH` + const externalConfigPath = ENV('APP_CONFIG_PATH', true); + if (isNotEmpty(externalConfigPath)) { + if (fs.existsSync(externalConfigPath)) { + overrideWithConfig(appConfig, externalConfigPath); + } else { + console.warn(`Unable to find external config file at ${externalConfigPath}`); + } + } + + // override with environment variables + overrideWithEnvironment(appConfig); + + // apply existing non convention UI environment variables + appConfig.ui.host = isNotEmpty(ENV('HOST', true)) ? ENV('HOST', true) : appConfig.ui.host; + appConfig.ui.port = isNotEmpty(ENV('PORT', true)) ? getNumberFromString(ENV('PORT', true)) : appConfig.ui.port; + appConfig.ui.nameSpace = isNotEmpty(ENV('NAMESPACE', true)) ? ENV('NAMESPACE', true) : appConfig.ui.nameSpace; + appConfig.ui.ssl = isNotEmpty(ENV('SSL', true)) ? getBooleanFromString(ENV('SSL', true)) : appConfig.ui.ssl; + + // apply existing non convention REST environment variables + appConfig.rest.host = isNotEmpty(ENV('REST_HOST', true)) ? ENV('REST_HOST', true) : appConfig.rest.host; + appConfig.rest.port = isNotEmpty(ENV('REST_PORT', true)) ? getNumberFromString(ENV('REST_PORT', true)) : appConfig.rest.port; + appConfig.rest.nameSpace = isNotEmpty(ENV('REST_NAMESPACE', true)) ? ENV('REST_NAMESPACE', true) : appConfig.rest.nameSpace; + appConfig.rest.ssl = isNotEmpty(ENV('REST_SSL', true)) ? getBooleanFromString(ENV('REST_SSL', true)) : appConfig.rest.ssl; + + // apply build defined production + appConfig.production = env === 'production'; + + // build base URLs + buildBaseUrl(appConfig.ui); + buildBaseUrl(appConfig.rest); + + if (isNotEmpty(destConfigPath)) { + fs.writeFileSync(destConfigPath, JSON.stringify(appConfig, null, 2)); + + console.log(`Angular ${colors.bold('config.json')} file generated correctly at ${colors.bold(destConfigPath)} \n`); + } + + return appConfig; +}; diff --git a/src/config/config.util.spec.ts b/src/config/config.util.spec.ts new file mode 100644 index 0000000000..7896b1f5c2 --- /dev/null +++ b/src/config/config.util.spec.ts @@ -0,0 +1,56 @@ +import { environment } from '../environments/environment.production'; +import { extendEnvironmentWithAppConfig } from './config.util'; +import { DefaultAppConfig } from './default-app-config'; +import { HandleThemeConfig } from './theme.model'; + +describe('Config Util', () => { + describe('extendEnvironmentWithAppConfig', () => { + it('should extend prod environment with app config', () => { + const appConfig = new DefaultAppConfig(); + expect(appConfig.cache.msToLive.default).toEqual(15 * 60 * 1000); // 15 minute + expect(appConfig.ui.rateLimiter.windowMs).toEqual(1 * 60 * 1000); // 1 minute + expect(appConfig.ui.rateLimiter.max).toEqual(500); + + expect(appConfig.submission.autosave.metadata).toEqual([]); + + expect(appConfig.themes.length).toEqual(1); + expect(appConfig.themes[0].name).toEqual('dspace'); + + const msToLive = 1 * 60 * 1000; // 1 minute + appConfig.cache.msToLive.default = msToLive; + + const rateLimiter = { + windowMs: 5 * 50 * 1000, // 5 minutes + max: 1000 + }; + appConfig.ui.rateLimiter = rateLimiter; + + const autoSaveMetadata = [ + 'dc.author', + 'dc.title' + ]; + + appConfig.submission.autosave.metadata = autoSaveMetadata; + + const customTheme: HandleThemeConfig = { + name: 'custom', + handle: '10673/1233' + }; + + appConfig.themes.push(customTheme); + + extendEnvironmentWithAppConfig(environment, appConfig); + + expect(environment.cache.msToLive.default).toEqual(msToLive); + expect(environment.ui.rateLimiter.windowMs).toEqual(rateLimiter.windowMs); + expect(environment.ui.rateLimiter.max).toEqual(rateLimiter.max); + expect(environment.submission.autosave.metadata[0]).toEqual(autoSaveMetadata[0]); + expect(environment.submission.autosave.metadata[1]).toEqual(autoSaveMetadata[1]); + + expect(environment.themes.length).toEqual(2); + expect(environment.themes[0].name).toEqual('dspace'); + expect(environment.themes[1].name).toEqual(customTheme.name); + expect((environment.themes[1] as HandleThemeConfig).handle).toEqual(customTheme.handle); + }); + }); +}); diff --git a/src/config/config.util.ts b/src/config/config.util.ts new file mode 100644 index 0000000000..c45282269c --- /dev/null +++ b/src/config/config.util.ts @@ -0,0 +1,54 @@ +import * as merge from 'deepmerge'; + +import { environment } from '../environments/environment'; + +import { hasNoValue } from '../app/shared/empty.util'; + +import { AppConfig } from './app-config.interface'; +import { ThemeConfig } from './theme.model'; + +/** + * Extend Angular environment with app config. + * + * @param env environment object + * @param appConfig app config + */ +const extendEnvironmentWithAppConfig = (env: any, appConfig: AppConfig): void => { + mergeConfig(env, appConfig); + console.log(`Environment extended with app config`); +}; + +/** + * Merge one config into another. + * + * @param destinationConfig destination config + * @param sourceConfig source config + */ +const mergeConfig = (destinationConfig: any, sourceConfig: AppConfig): void => { + const mergeOptions = { + arrayMerge: (destinationArray, sourceArray, options) => sourceArray + }; + Object.assign(destinationConfig, merge.all([ + destinationConfig, + sourceConfig + ], mergeOptions)); +}; + +/** + * Get default theme config from environment. + * + * @returns default theme config + */ +const getDefaultThemeConfig = (): ThemeConfig => { + return environment.themes.find((themeConfig: any) => + hasNoValue(themeConfig.regex) && + hasNoValue(themeConfig.handle) && + hasNoValue(themeConfig.uuid) + ); +}; + +export { + extendEnvironmentWithAppConfig, + mergeConfig, + getDefaultThemeConfig +}; diff --git a/src/environments/environment.common.ts b/src/config/default-app-config.ts similarity index 72% rename from src/environments/environment.common.ts rename to src/config/default-app-config.ts index 626b8cec32..9205df1002 100644 --- a/src/environments/environment.common.ts +++ b/src/config/default-app-config.ts @@ -1,72 +1,102 @@ -import { GlobalConfig } from '../config/global-config.interface'; -import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; +import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; +import { AppConfig } from './app-config.interface'; +import { AuthConfig } from './auth-config.interfaces'; +import { BrowseByConfig } from './browse-by-config.interface'; +import { CacheConfig } from './cache-config.interface'; +import { CollectionPageConfig } from './collection-page-config.interface'; +import { FormConfig } from './form-config.interfaces'; +import { ItemPageConfig } from './item-page-config.interface'; +import { LangConfig } from './lang-config.interface'; +import { MediaViewerConfig } from './media-viewer-config.interface'; +import { INotificationBoardOptions } from './notifications-config.interfaces'; +import { ServerConfig } from './server-config.interface'; +import { SubmissionConfig } from './submission-config.interface'; +import { ThemeConfig } from './theme.model'; +import { UIServerConfig } from './ui-server-config.interface'; +import { UniversalConfig } from './universal-config.interface'; -export const environment: GlobalConfig = { - production: true, - // Angular Universal server settings. - // NOTE: these must be "synced" with the 'dspace.ui.url' setting in your backend's local.cfg. - ui: { +export class DefaultAppConfig implements AppConfig { + production = false; + + // Angular Universal settings + universal: UniversalConfig = { + preboot: true, + async: true, + time: false + }; + + // NOTE: will log all redux actions and transfers in console + debug = false; + + // Angular Universal server settings + // NOTE: these must be 'synced' with the 'dspace.ui.url' setting in your backend's local.cfg. + ui: UIServerConfig = { ssl: false, host: 'localhost', port: 4000, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/', - // The rateLimiter settings limit each IP to a "max" of 500 requests per "windowMs" (1 minute). + + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). rateLimiter: { - windowMs: 1 * 60 * 1000, // 1 minute + windowMs: 1 * 60 * 1000, // 1 minute max: 500 // limit each IP to 500 requests per windowMs } - }, - // The REST API server settings. - // NOTE: these must be "synced" with the 'dspace.server.url' setting in your backend's local.cfg. - rest: { - ssl: true, - host: 'api7.dspace.org', - port: 443, + }; + + // The REST API server settings + // NOTE: these must be 'synced' with the 'dspace.server.url' setting in your backend's local.cfg. + rest: ServerConfig = { + ssl: false, + host: 'localhost', + port: 8080, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/server', - }, + nameSpace: '/', + }; + // Caching settings - cache: { + cache: CacheConfig = { // NOTE: how long should objects be cached for by default msToLive: { - default: 15 * 60 * 1000, // 15 minutes + default: 15 * 60 * 1000 // 15 minutes }, - // msToLive: 1000, // 15 minutes control: 'max-age=60', // revalidate browser autoSync: { defaultTime: 0, maxBufferSize: 100, - timePerMethod: {[RestRequestMethod.PATCH]: 3} as any // time in seconds + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds } - }, + }; + // Authentication settings - auth: { + auth: AuthConfig = { // Authentication UI settings ui: { // the amount of time before the idle warning is shown timeUntilIdle: 15 * 60 * 1000, // 15 minutes // the amount of time the user has to react after the idle warning is shown before they are logged out. - idleGracePeriod: 5 * 60 * 1000, // 5 minutes + idleGracePeriod: 5 * 60 * 1000 // 5 minutes }, // Authentication REST settings rest: { // If the rest token expires in less than this amount of time, it will be refreshed automatically. // This is independent from the idle warning. - timeLeftBeforeTokenRefresh: 2 * 60 * 1000, // 2 minutes - }, - }, + timeLeftBeforeTokenRefresh: 2 * 60 * 1000 // 2 minutes + } + }; + // Form settings - form: { + form: FormConfig = { // NOTE: Map server-side validators to comparative Angular form validators validatorMap: { required: 'required', regex: 'pattern' } - }, + }; + // Notifications - notifications: { + notifications: INotificationBoardOptions = { rtl: false, position: ['top', 'right'], maxStack: 8, @@ -75,9 +105,10 @@ export const environment: GlobalConfig = { clickToClose: true, // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' animate: NotificationAnimationsType.Scale - }, + }; + // Submission settings - submission: { + submission: SubmissionConfig = { autosave: { // NOTE: which metadata trigger an autosave metadata: [], @@ -135,89 +166,58 @@ export const environment: GlobalConfig = { { value: 'default', style: 'text-muted' - }, + } ] } } - }, - // Angular Universal settings - universal: { - preboot: true, - async: true, - time: false - }, - // NOTE: will log all redux actions and transfers in console - debug: false, + }; + // Default Language in which the UI will be rendered if the user's browser language is not an active language - defaultLanguage: 'en', + defaultLanguage = 'en'; + // Languages. DSpace Angular holds a message catalog for each of the following languages. // When set to active, users will be able to switch to the use of this language in the user interface. - languages: [{ - code: 'en', - label: 'English', - active: true, - }, { - code: 'cs', - label: 'Čeština', - active: true, - }, { - code: 'de', - label: 'Deutsch', - active: true, - }, { - code: 'es', - label: 'Español', - active: true, - }, { - code: 'fr', - label: 'Français', - active: true, - }, { - code: 'lv', - label: 'Latviešu', - active: true, - }, { - code: 'hu', - label: 'Magyar', - active: true, - }, { - code: 'nl', - label: 'Nederlands', - active: true, - }, { - code: 'pt-PT', - label: 'Português', - active: true, - },{ - code: 'pt-BR', - label: 'Português do Brasil', - active: true, - },{ - code: 'fi', - label: 'Suomi', - active: true, - }], + languages: LangConfig[] = [ + { code: 'en', label: 'English', active: true }, + { code: 'cs', label: 'Čeština', active: true }, + { code: 'de', label: 'Deutsch', active: true }, + { code: 'es', label: 'Español', active: true }, + { code: 'fr', label: 'Français', active: true }, + { code: 'lv', label: 'Latviešu', active: true }, + { code: 'hu', label: 'Magyar', active: true }, + { code: 'nl', label: 'Nederlands', active: true }, + { code: 'pt-PT', label: 'Português', active: true }, + { code: 'pt-BR', label: 'Português do Brasil', active: true }, + { code: 'fi', label: 'Suomi', active: true } + ]; + // Browse-By Pages - browseBy: { + browseBy: BrowseByConfig = { // Amount of years to display using jumps of one year (current year - oneYearLimit) oneYearLimit: 10, // Limit for years to display using jumps of five years (current year - fiveYearLimit) fiveYearLimit: 30, // The absolute lowest year to display in the dropdown (only used when no lowest date can be found for all items) - defaultLowerLimit: 1900, - }, - item: { + defaultLowerLimit: 1900 + }; + + // Item Page Config + item: ItemPageConfig = { edit: { undoTimeout: 10000 // 10 seconds } - }, - collection: { + }; + + // Collection Page Config + collection: CollectionPageConfig = { edit: { undoTimeout: 10000 // 10 seconds } - }, - themes: [ + }; + + // Theme Config + themes: ThemeConfig[] = [ // Add additional themes here. In the case where multiple themes match a route, the first one // in this list will get priority. It is advisable to always have a theme that matches // every route as the last one @@ -247,12 +247,12 @@ export const environment: GlobalConfig = { // name: 'custom-A', // extends: 'custom-B', // // Any of the matching properties above can be used - // handle: '10673/34', + // handle: '10673/34' // }, // { // name: 'custom-B', // extends: 'custom', - // handle: '10673/12', + // handle: '10673/12' // }, // { // // A theme with only a name will match every route @@ -305,12 +305,12 @@ export const environment: GlobalConfig = { }, ] }, - ], + ]; // Whether to enable media viewer for image and/or video Bitstreams (i.e. Bitstreams whose MIME type starts with "image" or "video"). // For images, this enables a gallery viewer where you can zoom or page through images. // For videos, this enables embedded video streaming - mediaViewer: { + mediaViewer: MediaViewerConfig = { image: false, - video: false, - }, -}; + video: false + }; +} diff --git a/src/environments/environment.production.ts b/src/environments/environment.production.ts new file mode 100644 index 0000000000..85c0f8fddc --- /dev/null +++ b/src/environments/environment.production.ts @@ -0,0 +1,12 @@ +import { AppConfig } from '../config/app-config.interface'; + +export const environment: Partial = { + production: true, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + } +}; diff --git a/src/environments/environment.template.ts b/src/environments/environment.template.ts deleted file mode 100644 index 6f6510e1ab..0000000000 --- a/src/environments/environment.template.ts +++ /dev/null @@ -1,10 +0,0 @@ -export const environment = { - /** - * TODO add the sections from environment.common.ts you want to override here - * e.g. - * rest: { - * host: 'rest.api', - * nameSpace: '/rest', - * } - */ -}; diff --git a/src/environments/mock-environment.ts b/src/environments/environment.test.ts similarity index 87% rename from src/environments/mock-environment.ts rename to src/environments/environment.test.ts index 02c12d9992..f8a3248837 100644 --- a/src/environments/mock-environment.ts +++ b/src/environments/environment.test.ts @@ -1,45 +1,65 @@ -// This configuration is only used for unit tests, end-to-end tests use environment.prod.ts +// This configuration is only used for unit tests, end-to-end tests use environment.production.ts import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; -import { GlobalConfig } from '../config/global-config.interface'; +import { AppConfig } from '../config/app-config.interface'; -export const environment: Partial = { - rest: { - ssl: true, - host: 'rest.com', - port: 443, - // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript - nameSpace: '/api', - baseUrl: 'https://rest.api/' +export const environment: AppConfig = { + production: false, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false }, + + // Angular Universal server settings. ui: { ssl: false, host: 'dspace.com', port: 80, // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript nameSpace: '/angular-dspace', - rateLimiter: undefined + baseUrl: 'http://dspace.com/angular-dspace', + // The rateLimiter settings limit each IP to a 'max' of 500 requests per 'windowMs' (1 minute). + rateLimiter: { + windowMs: 1 * 60 * 1000, // 1 minute + max: 500 // limit each IP to 500 requests per windowMs + } }, - // Caching settings + + // The REST API server settings. + rest: { + ssl: true, + host: 'rest.com', + port: 443, + // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript + nameSpace: '/api', + baseUrl: 'https://rest.com/api' + }, + + // Caching settings cache: { // NOTE: how long should objects be cached for by default msToLive: { default: 15 * 60 * 1000, // 15 minutes }, // msToLive: 1000, // 15 minutes - control: 'max-age=60', // revalidate browser + control: 'max-age=60', autoSync: { defaultTime: 0, maxBufferSize: 100, - timePerMethod: {[RestRequestMethod.PATCH]: 3} as any // time in seconds + timePerMethod: { [RestRequestMethod.PATCH]: 3 } as any // time in seconds } }, + // Authentication settings auth: { // Authentication UI settings ui: { // the amount of time before the idle warning is shown - timeUntilIdle: 20000, // 20 sec + timeUntilIdle: 20000, + // the amount of time the user has to react after the idle warning is shown before they are logged out. idleGracePeriod: 20000, // 20 sec }, @@ -50,6 +70,7 @@ export const environment: Partial = { timeLeftBeforeTokenRefresh: 20000, // 20 sec }, }, + // Form settings form: { // NOTE: Map server-side validators to comparative Angular form validators @@ -58,17 +79,19 @@ export const environment: Partial = { regex: 'pattern' } }, + // Notifications notifications: { rtl: false, position: ['top', 'right'], maxStack: 8, // NOTE: after how many seconds notification is closed automatically. If set to zero notifications are not closed automatically - timeOut: 5000, // 5 second + timeOut: 5000, clickToClose: true, // NOTE: 'fade' | 'fromTop' | 'fromRight' | 'fromBottom' | 'fromLeft' | 'rotate' | 'scale' animate: NotificationAnimationsType.Scale }, + // Submission settings submission: { autosave: { @@ -114,21 +137,17 @@ export const environment: Partial = { value: 'default', style: 'text-muted' }, - ] } } }, - // Angular Universal settings - universal: { - preboot: true, - async: true, - time: false - }, + // NOTE: will log all redux actions and transfers in console debug: false, + // Default Language in which the UI will be rendered if the user's browser language is not an active language defaultLanguage: 'en', + // Languages. DSpace Angular holds a message catalog for each of the following languages. // When set to active, users will be able to switch to the use of this language in the user interface. languages: [{ @@ -160,6 +179,7 @@ export const environment: Partial = { label: 'Latviešu', active: true, }], + // Browse-By Pages browseBy: { // Amount of years to display using jumps of one year (current year - oneYearLimit) @@ -207,5 +227,5 @@ export const environment: Partial = { mediaViewer: { image: true, video: true - }, + } }; diff --git a/src/environments/environment.ts b/src/environments/environment.ts new file mode 100644 index 0000000000..90eecac900 --- /dev/null +++ b/src/environments/environment.ts @@ -0,0 +1,26 @@ +// This file can be replaced during build by using the `fileReplacements` array. +// `ng build --configuration production` replaces `environment.ts` with `environment.production.ts`. +// `ng test --configuration test` replaces `environment.ts` with `environment.test.ts`. +// The list of file replacements can be found in `angular.json`. + +import { AppConfig } from '../config/app-config.interface'; + +export const environment: Partial = { + production: false, + + // Angular Universal settings + universal: { + preboot: true, + async: true, + time: false + } +}; + +/* + * For easier debugging in development mode, you can import the following file + * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. + * + * This import should be commented out in production mode because it will have a negative impact + * on performance if an error is thrown. + */ +// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/src/main.browser.ts b/src/main.browser.ts index d6c3fd6535..de4276ea93 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -1,22 +1,25 @@ import 'zone.js/dist/zone'; import 'reflect-metadata'; import 'core-js/es/reflect'; + import { enableProdMode } from '@angular/core'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { bootloader } from '@angularclass/bootloader'; - import { load as loadWebFont } from 'webfontloader'; + import { hasValue } from './app/shared/empty.util'; import { BrowserAppModule } from './modules/app/browser-app.module'; import { environment } from './environments/environment'; +import { AppConfig } from './config/app-config.interface'; +import { extendEnvironmentWithAppConfig } from './config/config.util'; -if (environment.production) { - enableProdMode(); -} +const bootstrap = () => platformBrowserDynamic() + .bootstrapModule(BrowserAppModule, { + preserveWhitespaces: true + }); -export function main() { +const main = () => { // Load fonts async // https://github.com/typekit/webfontloader#configuration loadWebFont({ @@ -25,13 +28,27 @@ export function main() { } }); - return platformBrowserDynamic().bootstrapModule(BrowserAppModule, {preserveWhitespaces:true}); -} + if (environment.production) { + enableProdMode(); + + return bootstrap(); + } else { + + return fetch('assets/config.json') + .then((response) => response.json()) + .then((appConfig: AppConfig) => { + + // extend environment with app config for browser when not prerendered + extendEnvironmentWithAppConfig(environment, appConfig); + + return bootstrap(); + }); + } +}; // support async tag or hmr if (hasValue(environment.universal) && environment.universal.preboot === false) { - bootloader(main); + main(); } else { - document.addEventListener('DOMContentLoaded', () => bootloader(main)); + document.addEventListener('DOMContentLoaded', main); } - diff --git a/src/main.ts b/src/main.ts deleted file mode 100644 index 734cd3c5f5..0000000000 --- a/src/main.ts +++ /dev/null @@ -1,20 +0,0 @@ -import 'core-js/es/reflect'; -import 'zone.js/dist/zone'; -import 'reflect-metadata'; - -import { enableProdMode } from '@angular/core'; -import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; - -import { AppModule } from './app/app.module'; -import { environment } from './environments/environment'; - -if (environment.production) { - enableProdMode(); -} - -document.addEventListener('DOMContentLoaded', () => { - document.addEventListener('DOMContentLoaded', () => { - platformBrowserDynamic().bootstrapModule(AppModule) - .catch((err) => console.error(err)); - }); -}); diff --git a/src/modules/app/browser-app.module.ts b/src/modules/app/browser-app.module.ts index efdaf12a1b..86497d4599 100644 --- a/src/modules/app/browser-app.module.ts +++ b/src/modules/app/browser-app.module.ts @@ -1,7 +1,8 @@ import { HttpClient, HttpClientModule } from '@angular/common/http'; -import { NgModule } from '@angular/core'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; import { BrowserModule, makeStateKey, TransferState } from '@angular/platform-browser'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { RouterModule, NoPreloading } from '@angular/router'; import { REQUEST } from '@nguniversal/express-engine/tokens'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; @@ -30,9 +31,13 @@ import { } from '../../app/core/services/browser-hard-redirect.service'; import { LocaleService } from '../../app/core/locale/locale.service'; import { GoogleAnalyticsService } from '../../app/statistics/google-analytics.service'; -import { RouterModule, NoPreloading } from '@angular/router'; import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { BrowserAuthRequestService } from '../../app/core/auth/browser-auth-request.service'; +import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; +import { DefaultAppConfig } from '../../config/default-app-config'; +import { extendEnvironmentWithAppConfig } from '../../config/config.util'; + +import { environment } from '../../environments/environment'; export const REQ_KEY = makeStateKey('req'); @@ -54,12 +59,12 @@ export function getRequest(transferState: TransferState): any { // forRoot ensures the providers are only created once IdlePreloadModule.forRoot(), RouterModule.forRoot([], { - // enableTracing: true, - useHash: false, - scrollPositionRestoration: 'enabled', - anchorScrolling: 'enabled', - preloadingStrategy: NoPreloading -}), + // enableTracing: true, + useHash: false, + scrollPositionRestoration: 'enabled', + anchorScrolling: 'enabled', + preloadingStrategy: NoPreloading + }), StatisticsModule.forRoot(), Angulartics2RouterlessModule.forRoot(), BrowserAnimationsModule, @@ -74,6 +79,20 @@ export function getRequest(transferState: TransferState): any { AppModule ], providers: [ + { + provide: APP_INITIALIZER, + useFactory: (transferState: TransferState) => { + if (transferState.hasKey(APP_CONFIG_STATE)) { + const appConfig = transferState.get(APP_CONFIG_STATE, new DefaultAppConfig()); + // extend environment with app config for browser + extendEnvironmentWithAppConfig(environment, appConfig); + } + + return () => true; + }, + deps: [TransferState], + multi: true + }, { provide: REQUEST, useFactory: getRequest, diff --git a/src/modules/app/server-app.module.ts b/src/modules/app/server-app.module.ts index dad3a60d5c..8c49554de9 100644 --- a/src/modules/app/server-app.module.ts +++ b/src/modules/app/server-app.module.ts @@ -1,38 +1,40 @@ -import { NgModule } from '@angular/core'; -import { BrowserModule } from '@angular/platform-browser'; +import { HTTP_INTERCEPTORS } from '@angular/common/http'; +import { APP_INITIALIZER, NgModule } from '@angular/core'; +import { BrowserModule, TransferState } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { ServerModule } from '@angular/platform-server'; +import { RouterModule } from '@angular/router'; import { TranslateLoader, TranslateModule } from '@ngx-translate/core'; +import { Angulartics2 } from 'angulartics2'; +import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; + import { AppComponent } from '../../app/app.component'; import { AppModule } from '../../app/app.module'; import { DSpaceServerTransferStateModule } from '../transfer-state/dspace-server-transfer-state.module'; import { DSpaceTransferState } from '../transfer-state/dspace-transfer-state.service'; - import { TranslateJson5UniversalLoader } from '../../ngx-translate-loaders/translate-json5-universal.loader'; import { CookieService } from '../../app/core/services/cookie.service'; import { ServerCookieService } from '../../app/core/services/server-cookie.service'; import { AuthService } from '../../app/core/auth/auth.service'; import { ServerAuthService } from '../../app/core/auth/server-auth.service'; - -import { Angulartics2GoogleAnalytics } from 'angulartics2/ga'; import { AngularticsProviderMock } from '../../app/shared/mocks/angulartics-provider.service.mock'; import { SubmissionService } from '../../app/submission/submission.service'; import { ServerSubmissionService } from '../../app/submission/server-submission.service'; import { Angulartics2DSpace } from '../../app/statistics/angulartics/dspace-provider'; import { ServerLocaleService } from '../../app/core/locale/server-locale.service'; import { LocaleService } from '../../app/core/locale/locale.service'; -import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { ForwardClientIpInterceptor } from '../../app/core/forward-client-ip/forward-client-ip.interceptor'; import { HardRedirectService } from '../../app/core/services/hard-redirect.service'; import { ServerHardRedirectService } from '../../app/core/services/server-hard-redirect.service'; -import { Angulartics2 } from 'angulartics2'; import { Angulartics2Mock } from '../../app/shared/mocks/angulartics2.service.mock'; -import { RouterModule } from '@angular/router'; import { AuthRequestService } from '../../app/core/auth/auth-request.service'; import { ServerAuthRequestService } from '../../app/core/auth/server-auth-request.service'; +import { AppConfig, APP_CONFIG_STATE } from '../../config/app-config.interface'; + +import { environment } from '../../environments/environment'; export function createTranslateLoader() { return new TranslateJson5UniversalLoader('dist/server/assets/i18n/', '.json5'); @@ -60,6 +62,16 @@ export function createTranslateLoader() { AppModule ], providers: [ + // Initialize app config and extend environment + { + provide: APP_INITIALIZER, + useFactory: (transferState: TransferState) => { + transferState.set(APP_CONFIG_STATE, environment as AppConfig); + return () => true; + }, + deps: [TransferState], + multi: true + }, { provide: Angulartics2, useClass: Angulartics2Mock diff --git a/tslint.json b/tslint.json index 36a32ed795..ef85c50002 100644 --- a/tslint.json +++ b/tslint.json @@ -170,9 +170,5 @@ "use-life-cycle-interface": false, "no-outputs-metadata-property": true, "use-pipe-transform-interface": true - // "rxjs-collapse-imports": true, - // "rxjs-pipeable-operators-only": true, - // "rxjs-no-static-observable-methods": true, - // "rxjs-proper-imports": true } } diff --git a/webpack/webpack.browser.ts b/webpack/webpack.browser.ts index bc281e9b43..a71d749347 100644 --- a/webpack/webpack.browser.ts +++ b/webpack/webpack.browser.ts @@ -1,8 +1,16 @@ +import { join } from 'path'; + +import { buildAppConfig } from '../src/config/config.server'; import { commonExports } from './webpack.common'; module.exports = Object.assign({}, commonExports, { target: 'web', node: { module: 'empty' - } + }, + devServer: { + before(app, server) { + buildAppConfig(join(process.cwd(), 'src/assets/config.json')); + } + } }); diff --git a/yarn.lock b/yarn.lock index f467fb9840..0f8ef0a6e6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -427,11 +427,6 @@ dependencies: tslib "^2.0.0" -"@angularclass/bootloader@1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@angularclass/bootloader/-/bootloader-1.0.1.tgz#75de7cf3901b445900a419c2aeca44181d465060" - integrity sha1-dd5885AbRFkApBnCrspEGB1GUGA= - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.8.3": version "7.16.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.0.tgz#0dfc80309beec8411e65e706461c408b0bb9b431" @@ -1457,9 +1452,9 @@ integrity sha512-5a6wqoJV/xEdbRNKVo6I4hO3VjyDq//8q2f9I6PBAvMesJHFauXDorcNCsr9RzvsZnaWi5NYCcfyqP1QeFHFbw== "@cypress/request@^2.88.6": - version "2.88.7" - resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.7.tgz#386d960ab845a96953723348088525d5a75aaac4" - integrity sha512-FTULIP2rnDJvZDT9t6B4nSfYR40ue19tVmv3wUcY05R9/FPCoMl1nAPJkzWzBCo7ltVn5ThQTbxiMoGBN7k0ig== + version "2.88.10" + resolved "https://registry.yarnpkg.com/@cypress/request/-/request-2.88.10.tgz#b66d76b07f860d3a4b8d7a0604d020c662752cce" + integrity sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg== dependencies: aws-sign2 "~0.7.0" aws4 "^1.8.0" @@ -1468,8 +1463,7 @@ extend "~3.0.2" forever-agent "~0.6.1" form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" + http-signature "~1.3.6" is-typedarray "~1.0.0" isstream "~0.1.2" json-stringify-safe "~5.0.1" @@ -1507,9 +1501,9 @@ integrity sha512-HyYEUDeIj5rRQU2Hk5HTB2uHsbRQpF70nvMhVzi+VJR0X+xNEhjPui4/kBf3VeH/wqD28PT4sVOm8qqLjBrSZg== "@discoveryjs/json-ext@^0.5.0": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.5.tgz#9283c9ce5b289a3c4f61c12757469e59377f81f3" - integrity sha512-6nFkfkmSeV/rqSaS4oWHgmpnYw194f6hmWF5is6b0J1naJZoiD0NTc9AiUwPHvWsowkjuHErCZT1wa0jg+BLIA== + version "0.5.6" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz#d5e0706cf8c6acd8c6032f8d54070af261bbbb2f" + integrity sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA== "@edsilv/http-status-codes@^1.0.3": version "1.0.3" @@ -1532,9 +1526,9 @@ integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw== "@iiif/vocabulary@^1.0.20": - version "1.0.21" - resolved "https://registry.yarnpkg.com/@iiif/vocabulary/-/vocabulary-1.0.21.tgz#5808f62da11b64ca42a895025844be088df9f48a" - integrity sha512-XUD8RYeBiEzv8rdpC9tNBaQ0FMZuGWIkPsmcFhnqXW+5WjZmRLhgqycuoGwIjxt9nPsDgW1IxuiWduNgbLl9UA== + version "1.0.22" + resolved "https://registry.yarnpkg.com/@iiif/vocabulary/-/vocabulary-1.0.22.tgz#743d6cb1914e090137c56a4e44decbe7f8b21bdc" + integrity sha512-KOIZgRpDERMK4YGeSwPBNjpJETapMWp8pvEvSGUQkuTlqkiNLirZH65CIcEENZEL5A6mxrYLJX6okDYq7W1+RA== "@istanbuljs/schema@^0.1.2": version "0.1.3" @@ -1989,9 +1983,9 @@ integrity sha512-M6x29Vk4681dght4IMnPIcF1SNmeEm0c4uatlTFhp+++H1oDK1THEIzuCC2WeCBVhX+gU0NndsseDS3zaCtlcQ== "@types/express-serve-static-core@^4.17.18": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" - integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== + version "4.17.26" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.26.tgz#5d9a8eeecb9d5f9d7fc1d85f541512a84638ae88" + integrity sha512-zeu3tpouA043RHxW0gzRxwCHchMgftE8GArRsvYT0ByDMbn19olQHx5jLue0LxWY6iYtXb7rXmuVtSkhy9YZvQ== dependencies: "@types/node" "*" "@types/qs" "*" @@ -2083,14 +2077,14 @@ integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== "@types/node@*": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== + version "16.11.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.12.tgz#ac7fb693ac587ee182c3780c26eb65546a1a3c10" + integrity sha512-+2Iggwg7PxoO5Kyhvsq9VarmPbIelXP070HMImEpbtGCoyWNINQj4wzjbQCXzdHTRXnqufutJb5KAURZANNBAw== "@types/node@^14.14.31", "@types/node@^14.14.9": - version "14.17.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" - integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== + version "14.18.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.0.tgz#98df2397f6936bfbff4f089e40e06fa5dd88d32a" + integrity sha512-0GeIl2kmVMXEnx8tg1SlG6Gg8vkqirrW752KqolYo1PHevhhZN3bhJ67qHj+bQaINhX0Ra3TlWwRvMCd9iEfNQ== "@types/parse-json@^4.0.0": version "4.0.0" @@ -2140,9 +2134,9 @@ "@types/react" "*" "@types/react@*": - version "17.0.35" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.35.tgz#217164cf830267d56cd1aec09dcf25a541eedd4c" - integrity sha512-r3C8/TJuri/SLZiiwwxQoLAoavaczARfT9up9b4Jr65+ErAUX3MIkU0oMOQnrpfgHme8zIqZLX7O5nnjm5Wayw== + version "17.0.37" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.37.tgz#6884d0aa402605935c397ae689deed115caad959" + integrity sha512-2FS1oTqBGcH/s0E+CjrCCR9+JMpsu9b69RTFO+40ua43ZqP5MmQ4iUde/dMjWR909KxZwmOQIFq6AV6NjEG5xg== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -2474,9 +2468,9 @@ acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.0.4, acorn@^8.2.4: - version "8.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.5.0.tgz#4512ccb99b3698c752591e9bb4472e38ad43cee2" - integrity sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q== + version "8.6.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.6.0.tgz#e3692ba0eb1a0c83eaa4f37f5fa7368dd7142895" + integrity sha512-U1riIR+lBSNi3IbxtaHOIKdH8sLFv3NYfNv8sg7ZsNhcfl4HF2++BfqqrNAxoCLQW1iiylOj76ecnaUxz+z9yw== adjust-sourcemap-loader@3.0.0: version "3.0.0" @@ -2560,9 +2554,9 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== ajv-keywords@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.0.0.tgz#d01b3b21715b2f63d02aa511b82fc6eb3b30083c" - integrity sha512-ULd1QMjRoH6JDNUQIfDLrlE+OgZlFaxyYCjzt58uNuUQtKXt8/U+vK/8Ql0gyn/C5mqZzUWtKMqr/4YquvTrWA== + version "5.1.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== dependencies: fast-deep-equal "^3.1.3" @@ -2597,9 +2591,9 @@ ajv@8.6.2: uri-js "^4.2.2" ajv@^8.0.0, ajv@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.1.tgz#e73dd88eeb4b10bbcd82bee136e6fbe801664d18" - integrity sha512-6CiMNDrzv0ZR916u2T+iRunnD60uWmNn8SkdB44/6stVORUg0aAkWO7PkOhpCmjmW8f2I/G/xnowD66fxGyQJg== + version "8.8.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.8.2.tgz#01b4fef2007a28bf75f0b7fc009f62679de4abbb" + integrity sha512-x9VuX+R/jcFj1DHo/fCp99esgGDWiHENrKxaCENuCxpoMCmAt/COCGVDwA7kleEpEzJjDnvh3yGoOuLu0Dtllw== dependencies: fast-deep-equal "^3.1.1" json-schema-traverse "^1.0.0" @@ -2759,6 +2753,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + aria-query@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" @@ -3375,7 +3374,7 @@ browserify-zlib@^0.2.0: dependencies: pako "~1.0.5" -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.0, browserslist@^4.16.1, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.17.6, browserslist@^4.6.4, browserslist@^4.9.1: +browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.16.0, browserslist@^4.16.1, browserslist@^4.16.6, browserslist@^4.17.5, browserslist@^4.18.1, browserslist@^4.6.4, browserslist@^4.9.1: version "4.18.1" resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.18.1.tgz#60d3920f25b6860eb917c6c7b185576f4d8b017f" integrity sha512-8ScCzdpPwR2wQh8IT82CA2VgDwjHyqMovPBZSNH54+tm4Jk2pCuv90gmAdH6J84OCRWi0b4gMe6O6XPXuJnjgQ== @@ -3661,9 +3660,9 @@ caniuse-api@^3.0.0: lodash.uniq "^4.5.0" caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001165, caniuse-lite@^1.0.30001181, caniuse-lite@^1.0.30001280: - version "1.0.30001282" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001282.tgz#38c781ee0a90ccfe1fe7fefd00e43f5ffdcb96fd" - integrity sha512-YhF/hG6nqBEllymSIjLtR2iWDDnChvhnVJqp+vloyt2tEHFG1yBR+ac2B/rOw0qOK0m0lEXU2dv4E/sMk5P9Kg== + version "1.0.30001285" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001285.tgz#fe1e52229187e11d6670590790d669b9e03315b7" + integrity sha512-KAOkuUtcQ901MtmvxfKD+ODHH9YVDYnBt+TGYSz2KIfnq22CiArbUxXPN9067gNbgMlnNYRSwho8OPXZPALB9Q== canonical-path@1.0.0: version "1.0.0" @@ -3780,9 +3779,9 @@ ci-info@^2.0.0: integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== ci-info@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.2.0.tgz#2876cb948a498797b5236f0095bc057d0dca38b6" - integrity sha512-dVqRX7fLUm8J6FgHJ418XuIgDLZDkYcDFTeL6TA2gt5WlIZUQrrH6EZrNClwT/H0FateUsZkGIOPRrLbP+PR9A== + version "3.3.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2" + integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw== cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: version "1.0.4" @@ -4007,9 +4006,9 @@ color-name@^1.0.0, color-name@~1.1.4: integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== color-string@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.6.0.tgz#c3915f61fe267672cb7e1e064c9d692219f6c312" - integrity sha512-c/hGS+kRWJutUBEngKKmk4iH3sD59MBkoxVapS/0wgpCz2u7XsNloxknyvBhzwEs1IbV36D9PwqLPJ2DTu3vMA== + version "1.9.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.0.tgz#63b6ebd1bec11999d1df3a79a7569451ac2be8aa" + integrity sha512-9Mrz2AQLefkH1UvASKj6v6hj/7eWgjnT/cVsR8CumieLoT+g900exWeNogqtweI8dxloXN9BDQTYro1oWu/5CQ== dependencies: color-name "^1.0.0" simple-swizzle "^0.2.2" @@ -4342,11 +4341,11 @@ copy-webpack-plugin@^6.4.1: webpack-sources "^1.4.3" core-js-compat@^3.6.2, core-js-compat@^3.8.0: - version "3.19.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476" - integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g== + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.3.tgz#de75e5821c5ce924a0a1e7b7d5c2cb973ff388aa" + integrity sha512-59tYzuWgEEVU9r+SRgceIGXSSUn47JknoiXW6Oq7RW8QHjXWz3/vp8pa7dbtuVu40sewz3OP3JmQEcDdztrLhA== dependencies: - browserslist "^4.17.6" + browserslist "^4.18.1" semver "7.0.0" core-js@3.6.4: @@ -4365,9 +4364,9 @@ core-js@^2.4.0: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.7.0: - version "3.19.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.1.tgz#f6f173cae23e73a7d88fa23b6e9da329276c6641" - integrity sha512-Tnc7E9iKd/b/ff7GFbhwPVzJzPztGrChB8X8GLqoYGdEOG8IpLnK1xPyo3ZoO3HsK6TodJS58VGPOxA+hLHQMg== + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.3.tgz#6df8142a996337503019ff3235a7022d7cdf4559" + integrity sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g== core-util-is@1.0.2: version "1.0.2" @@ -4470,7 +4469,14 @@ critters@0.0.7: parse5-htmlparser2-tree-adapter "^6.0.1" pretty-bytes "^5.3.0" -cross-spawn@^6.0.0, cross-spawn@^6.0.5: +cross-env@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== + dependencies: + cross-spawn "^7.0.1" + +cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== @@ -4481,7 +4487,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4773,9 +4779,9 @@ cssnano-preset-default@^4.0.7, cssnano-preset-default@^4.0.8: postcss-unique-selectors "^4.0.1" cssnano-preset-default@^5.0.1: - version "5.1.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.7.tgz#68c3ad1ec6a810482ec7d06b2d70fc34b6b0d70c" - integrity sha512-bWDjtTY+BOqrqBtsSQIbN0RLGD2Yr2CnecpP0ydHNafh9ZUEre8c8VYTaH9FEbyOt0eIfEUAYYk5zj92ioO8LA== + version "5.1.8" + resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-5.1.8.tgz#7525feb1b72f7b06e57f55064cbdae341d79dea2" + integrity sha512-zWMlP0+AMPBVE852SqTrP0DnhTcTA2C1wAF92TKZ3Va+aUVqLIhkqKlnJIXXdqXD7RN+S1ujuWmNpvrJBiM/vg== dependencies: css-declaration-sorter "^6.0.3" cssnano-utils "^2.0.1" @@ -4802,7 +4808,7 @@ cssnano-preset-default@^5.0.1: postcss-normalize-url "^5.0.3" postcss-normalize-whitespace "^5.0.1" postcss-ordered-values "^5.0.2" - postcss-reduce-initial "^5.0.1" + postcss-reduce-initial "^5.0.2" postcss-reduce-transforms "^5.0.1" postcss-svgo "^5.0.3" postcss-unique-selectors "^5.0.2" @@ -4888,9 +4894,9 @@ cssstyle@^2.3.0: cssom "~0.3.6" csstype@^2.5.2: - version "2.6.18" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.18.tgz#980a8b53085f34af313410af064f2bd241784218" - integrity sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ== + version "2.6.19" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa" + integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ== csstype@^3.0.2: version "3.0.10" @@ -5018,10 +5024,10 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" -debug@4, debug@4.3.2, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.2: + version "4.3.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" + integrity sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q== dependencies: ms "2.1.2" @@ -5032,6 +5038,13 @@ debug@4.3.1: dependencies: ms "2.1.2" +debug@4.3.2: + version "4.3.2" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" + integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== + dependencies: + ms "2.1.2" + debug@^3.1.0, debug@^3.1.1, debug@^3.2.5, debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -5115,7 +5128,7 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" -default-gateway@^6.0.0: +default-gateway@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== @@ -5398,9 +5411,9 @@ domhandler@^3.0.0: domelementtype "^2.0.1" domhandler@^4.0.0, domhandler@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.2.tgz#e825d721d19a86b8c201a35264e226c678ee755f" - integrity sha512-PzE9aBMsdZO8TK4BnuJwH0QT41wgMbRzuZrHUcpYncEjmQazq8QEaBWgLG7ZyC/DAZKEgglpIA6j4Qn/HmxS3w== + version "4.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.0.tgz#16c658c626cf966967e306f966b431f77d4a5626" + integrity sha512-fC0aXNQXqKSFTr2wDNZDhsEYjCiYsDWl3D01kwt25hm1YIPyDGHvvi3rw+PLqHAl/m71MaiF7d5zvBr0p5UB2g== dependencies: domelementtype "^2.2.0" @@ -5410,9 +5423,9 @@ domino@^2.1.2: integrity sha512-3VdM/SXBZX2omc9JF9nOPCtDaYQ67BGp5CoLpIQlO2KCAPETs8TcDHacF26jXadGbvUteZzRTeos2fhID5+ucQ== dompurify@^2.0.11: - version "2.3.3" - resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c" - integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg== + version "2.3.4" + resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.4.tgz#1cf5cf0105ccb4debdf6db162525bd41e6ddacc6" + integrity sha512-6BVcgOAVFXjI0JTjEvZy901Rghm+7fDQOrNIcxB4+gdhj6Kwp6T9VBhBY/AbagKHJocRkDYGd6wvI+p4/10xtQ== domutils@^1.7.0: version "1.7.0" @@ -5499,9 +5512,9 @@ ee-first@1.1.1: integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= electron-to-chromium@^1.3.896: - version "1.3.901" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.901.tgz#ce2c3157d61bce9f42f1e83225c17358ae9f4918" - integrity sha512-ToJdV2vzwT2jeAsw8zIggTFllJ4Kxvwghk39AhJEHHlIxor10wsFI3wo69p8nFc0s/ATWBqugPv/k3nW4Y9Mww== + version "1.4.12" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.12.tgz#5f73d1278c6205fc41d7a0ebd75563046b77c5d8" + integrity sha512-zjfhG9Us/hIy8AlQ5OzfbR/C4aBv1Dg/ak4GX35CELYlJ4tDAtoEcQivXvyBdqdNQ+R6PhlgQqV8UNPJmhkJog== element-resize-detector@^1.2.1: version "1.2.3" @@ -6287,9 +6300,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= fork-ts-checker-webpack-plugin@^6.0.3: - version "6.4.0" - resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.4.0.tgz#057e477cf1d8b013b2ed2669437f818680289c4c" - integrity sha512-3I3wFkc4DbzaUDPWEi96wdYGu4EKtxBafhZYm0o4mX51d9bphAY4P3mBl8K5mFXFJqVzHfmdbm9kLGnm7vwwBg== + version "6.5.0" + resolved "https://registry.yarnpkg.com/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.0.tgz#0282b335fa495a97e167f69018f566ea7d2a2b5e" + integrity sha512-cS178Y+xxtIjEUorcHddKS7yCMlrDPV31mt47blKKRfMd70Kxu5xruAFE2o9sDY6wVC5deuob/u/alD04YYHnw== dependencies: "@babel/code-frame" "^7.8.3" "@types/json-schema" "^7.0.5" @@ -6827,11 +6840,6 @@ hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2: dependencies: react-is "^16.7.0" -hosted-git-info@^2.1.4: - version "2.8.9" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" - integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== - hosted-git-info@^3.0.6: version "3.0.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" @@ -7007,9 +7015,9 @@ http-errors@~1.7.2: toidentifier "1.0.0" http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== + version "0.5.5" + resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.5.tgz#d7c30d5d3c90d865b4a2e870181f9d6f22ac7ac5" + integrity sha512-x+JVEkO2PoM8qqpbPbOL3cqHPwerep7OwzK7Ay+sMQjKzaKCqWvjoXm5tqMP9tXWWTnTzAjIhXg+J99XYuPhPA== http-proxy-agent@^4.0.1: version "4.0.1" @@ -7070,6 +7078,15 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" +http-signature@~1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9" + integrity sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw== + dependencies: + assert-plus "^1.0.0" + jsprim "^2.0.2" + sshpk "^1.14.1" + https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -7355,16 +7372,6 @@ internal-ip@^4.3.0: default-gateway "^4.2.0" ipaddr.js "^1.9.0" -internal-ip@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-6.2.0.tgz#d5541e79716e406b74ac6b07b856ef18dc1621c1" - integrity sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg== - dependencies: - default-gateway "^6.0.0" - ipaddr.js "^1.9.1" - is-ip "^3.1.0" - p-event "^4.2.0" - internal-slot@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" @@ -7396,17 +7403,12 @@ ip-regex@^2.1.0: resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip-regex@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -ipaddr.js@1.9.1, ipaddr.js@^1.9.0, ipaddr.js@^1.9.1: +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -7645,13 +7647,6 @@ is-interactive@^1.0.0: resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== -is-ip@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-ip/-/is-ip-3.1.0.tgz#2ae5ddfafaf05cb8008a62093cf29734f657c5d8" - integrity sha512-35vd5necO7IitFPjd/YBeqwWnyDWbuLH9ZXQdMfDA8TEo7pv5X8yfrvVO3xbJbLUlERCMvf6X0hTUamQxCYJ9Q== - dependencies: - ip-regex "^4.0.0" - is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" @@ -7935,9 +7930,9 @@ istanbul-lib-source-maps@^3.0.6: source-map "^0.6.1" istanbul-reports@^3.0.2: - version "3.0.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.5.tgz#a2580107e71279ea6d661ddede929ffc6d693384" - integrity sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ== + version "3.1.1" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.1.tgz#7085857f17d2441053c6ce5c3b8fdf6882289397" + integrity sha512-q1kvhAXWSsXfMjCdNHNPKZZv94OlspKnoGv+R9RGbnqOOQ0VbNfLFgQDVgi7hHenKsndGq3/o0OBdzDXthWcNw== dependencies: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" @@ -8029,6 +8024,13 @@ js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8102,10 +8104,10 @@ json-schema-traverse@^1.0.0: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= +json-schema@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" + integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== json-stringify-safe@~5.0.1: version "5.0.1" @@ -8182,13 +8184,23 @@ jsonschema@1.4.0: integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= + version "1.4.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" + integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== dependencies: assert-plus "1.0.0" extsprintf "1.3.0" - json-schema "0.2.3" + json-schema "0.4.0" + verror "1.10.0" + +jsprim@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-2.0.2.tgz#77ca23dbcd4135cd364800d22ff82c2185803d4d" + integrity sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ== + dependencies: + assert-plus "1.0.0" + extsprintf "1.3.0" + json-schema "0.4.0" verror "1.10.0" jss-plugin-camel-case@^10.5.1: @@ -8535,34 +8547,24 @@ limiter@^1.0.5: integrity sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA== lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= + version "1.2.4" + resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== listr2@^3.8.3: - version "3.13.4" - resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.13.4.tgz#34101fc0184545597e00d1e7915ccfbfb17332e6" - integrity sha512-lZ1Rut1DSIRwbxQbI8qaUBfOWJ1jEYRgltIM97j6kKOCI2pHVWMyxZvkU/JKmRBWcIYgDS2PK+yDgVqm7u3crw== + version "3.13.5" + resolved "https://registry.yarnpkg.com/listr2/-/listr2-3.13.5.tgz#105a813f2eb2329c4aae27373a281d610ee4985f" + integrity sha512-3n8heFQDSk+NcwBn3CgxEibZGaRzx+pC64n3YjpMD1qguV4nWus3Al+Oo3KooqFKTQEJ1v7MmnbnyyNspgx3NA== dependencies: cli-truncate "^2.1.0" - clone "^2.1.2" colorette "^2.0.16" log-update "^4.0.0" p-map "^4.0.0" + rfdc "^1.3.0" rxjs "^7.4.0" through "^2.3.8" wrap-ansi "^7.0.0" -load-json-file@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" - integrity sha1-L19Fq5HjMhYjT9U62rZo607AmTs= - dependencies: - graceful-fs "^4.1.2" - parse-json "^4.0.0" - pify "^3.0.0" - strip-bom "^3.0.0" - loader-runner@^2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" @@ -8731,9 +8733,9 @@ log4js@^6.2.1: streamroller "^2.2.4" loglevel@^1.6.8: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== + version "1.8.0" + resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" + integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: version "1.4.0" @@ -8877,9 +8879,9 @@ media-typer@0.3.0: integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= memfs@^3.1.2, memfs@^3.2.2: - version "3.3.0" - resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.3.0.tgz#4da2d1fc40a04b170a56622c7164c6be2c4cbef2" - integrity sha512-BEE62uMfKOavX3iG7GYX43QJ+hAeeWnwIAuJ/R6q96jaMtiLzhsxHJC8B1L7fK7Pt/vXDRwb3SG/yBpNGDPqzg== + version "3.4.0" + resolved "https://registry.yarnpkg.com/memfs/-/memfs-3.4.0.tgz#8bc12062b973be6b295d4340595736a656f0a257" + integrity sha512-o/RfP0J1d03YwsAxyHxAYs2kyJp55AFkMazlFAZFR2I2IXkxiUTXRabJ6RmNNCQ83LAD2jy52Khj0m3OffpNdA== dependencies: fs-monkey "1.0.3" @@ -8904,11 +8906,6 @@ memory-fs@^0.5.0: errno "^0.1.3" readable-stream "^2.0.1" -memorystream@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" - integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -8998,7 +8995,7 @@ mime@1.6.0, mime@^1.4.1: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.3.1, mime@^2.4.4, mime@^2.4.5: +mime@^2.4.4, mime@^2.4.5: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -9244,6 +9241,11 @@ move-concurrently@^1.0.1: rimraf "^2.5.4" run-queue "^1.0.3" +mrmime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.0.tgz#14d387f0585a5233d291baba339b063752a2398b" + integrity sha512-a70zx7zFfVO7XpnQ2IX1Myh9yY4UYvfld/dikWRnsXxbyvMcfz+u6UfgNAtH+k2QqtJuzVpv6eLTx1G2+WKZbQ== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -9457,7 +9459,7 @@ node-releases@^2.0.1: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -nodemon@^2.0.2: +nodemon@^2.0.15: version "2.0.15" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.15.tgz#504516ce3b43d9dc9a955ccd9ec57550a31a8d4e" integrity sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA== @@ -9487,16 +9489,6 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.3.2: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" @@ -9624,21 +9616,6 @@ npm-registry-fetch@^9.0.0: minizlib "^2.0.0" npm-package-arg "^8.0.0" -npm-run-all@^4.1.5: - version "4.1.5" - resolved "https://registry.yarnpkg.com/npm-run-all/-/npm-run-all-4.1.5.tgz#04476202a15ee0e2e214080861bff12a51d98fba" - integrity sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ== - dependencies: - ansi-styles "^3.2.1" - chalk "^2.4.1" - cross-spawn "^6.0.5" - memorystream "^0.3.1" - minimatch "^3.0.4" - pidtree "^0.3.0" - read-pkg "^3.0.0" - shell-quote "^1.6.1" - string.prototype.padend "^3.0.0" - npm-run-path@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" @@ -9712,9 +9689,9 @@ object-copy@^0.1.0: kind-of "^3.0.3" object-inspect@^1.11.0, object-inspect@^1.9.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.0.tgz#9dceb146cedd4148a0d9e51ab88d34cf509922b1" - integrity sha512-jp7ikS6Sd3GxQfZJPyH3cjcbJF6GZPClgdV+EFygjFLQ5FmW/dRUnTd9PQ9k0JhoNDabWFbpF1yCdSWCC6gexg== + version "1.11.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.11.1.tgz#d4bd7d7de54b9a75599f59a00bd698c1f1c6549b" + integrity sha512-If7BjFlpkzzBeV1cqgT3OSWT3azyoxDGajR+iGnFBfVV2EWyDyWaZZW2ERDjUaY2QM8i5jI3Sj7mhsM4DDAqWA== object-is@^1.0.1: version "1.1.5" @@ -9942,13 +9919,6 @@ p-cancelable@^1.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== -p-event@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" - integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== - dependencies: - p-timeout "^3.1.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -10016,13 +9986,6 @@ p-retry@^4.5.0: "@types/retry" "^0.12.0" retry "^0.13.1" -p-timeout@^3.1.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" - integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== - dependencies: - p-finally "^1.0.0" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -10236,13 +10199,6 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - path-type@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" @@ -10294,21 +10250,11 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== -pidtree@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/pidtree/-/pidtree-0.3.1.tgz#ef09ac2cc0533df1f3250ccf2c4d366b0d12114a" - integrity sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA== - pify@^2.0.0, pify@^2.2.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - pify@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" @@ -11152,12 +11098,12 @@ postcss-reduce-initial@^4.0.3: has "^1.0.0" postcss "^7.0.0" -postcss-reduce-initial@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.1.tgz#9d6369865b0f6f6f6b165a0ef5dc1a4856c7e946" - integrity sha512-zlCZPKLLTMAqA3ZWH57HlbCjkD55LX9dsRyxlls+wfuRfqCi5mSlZVan0heX5cHr154Dq9AfbH70LyhrSAezJw== +postcss-reduce-initial@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-5.0.2.tgz#fa424ce8aa88a89bc0b6d0f94871b24abe94c048" + integrity sha512-v/kbAAQ+S1V5v9TJvbGkV98V2ERPdU6XvMcKMjqAlYiJ2NtsHGlKYLPjWWcXlaTKNxooId7BGxeraK8qXvzKtw== dependencies: - browserslist "^4.16.0" + browserslist "^4.16.6" caniuse-api "^3.0.0" postcss-reduce-transforms@^4.0.2: @@ -11286,9 +11232,9 @@ postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== + version "4.2.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" + integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== postcss-values-parser@^2.0.0, postcss-values-parser@^2.0.1: version "2.0.1" @@ -11344,13 +11290,13 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2 source-map "^0.6.1" postcss@^8.1.4, postcss@^8.3.7: - version "8.3.11" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.3.11.tgz#c3beca7ea811cd5e1c4a3ec6d2e7599ef1f8f858" - integrity sha512-hCmlUAIlUiav8Xdqw3Io4LcpA1DOt7h3LSTAC4G6JGHFFaWzI6qvFt9oilvl8BmkbBRX1IhM90ZAmpk68zccQA== + version "8.4.4" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.4.tgz#d53d4ec6a75fd62557a66bb41978bf47ff0c2869" + integrity sha512-joU6fBsN6EIer28Lj6GDFoC/5yOZzLCfn0zHAn/MYXI7aPt4m4hK5KC5ovEZXy+lnCjmYIbQWngvju2ddyEr8Q== dependencies: nanoid "^3.1.30" picocolors "^1.0.0" - source-map-js "^0.6.2" + source-map-js "^1.0.1" prelude-ls@~1.1.2: version "1.1.2" @@ -11798,9 +11744,9 @@ react-full-screen@^0.2.4: fscreen "^1.0.1" react-i18next@^11.7.0: - version "11.14.2" - resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.2.tgz#2ff28f6a0ddf06eaf79435ed6c70c441d709cf34" - integrity sha512-fmDhwNA0zDmSEL3BBT5qwNMvxrKu25oXDDAZyHprfB0AHZmWXfBmRLf8MX8i1iBd2I2C2vsA2D9wxYBIwzooEQ== + version "11.14.3" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.3.tgz#b44b5c4d1aadac5211be011827a2830be60f2522" + integrity sha512-Hf2aanbKgYxPjG8ZdKr+PBz9sY6sxXuZWizxCYyJD2YzvJ0W9JTQcddVEjDaKyBoCyd3+5HTerdhc9ehFugc6g== dependencies: "@babel/runtime" "^7.14.5" html-parse-stringify "^3.0.1" @@ -11926,15 +11872,6 @@ read-package-json-fast@^2.0.1: json-parse-even-better-errors "^2.3.0" npm-normalize-package-bin "^1.0.1" -read-pkg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" - integrity sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k= - dependencies: - load-json-file "^4.0.0" - normalize-package-data "^2.3.2" - path-type "^3.0.0" - "readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" @@ -11993,9 +11930,9 @@ redux-saga@^1.1.3: "@redux-saga/core" "^1.1.3" redux-thunk@^2.3.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.0.tgz#ac89e1d6b9bdb9ee49ce69a69071be41bbd82d67" - integrity sha512-/y6ZKQNU/0u8Bm7ROLq9Pt/7lU93cT0IucYMrubo89ENjxPa7i8pqLKu6V4X7/TvYovQ6x01unTeyeZ9lgXiTA== + version "2.4.1" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.4.1.tgz#0dd8042cf47868f4b29699941de03c9301a75714" + integrity sha512-OOYGNY5Jy2TWvTL1KgAlVy6dcx3siPJ1wTq741EPyUKfn6W6nChdICjZwCd0p8AZBs5kWpZlbkXW2nE/zjUa+Q== redux@^4.0.0, redux@^4.0.4, redux@^4.0.5: version "4.1.2" @@ -12187,9 +12124,9 @@ requires-port@^1.0.0: integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= reselect@^4.0.0: - version "4.1.4" - resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.4.tgz#66df0aff41b6ee0f51e2cc17cfaf2c1995916f32" - integrity sha512-i1LgXw8DKSU5qz1EV0ZIKz4yIUHJ7L3bODh+Da6HmVSm9vdL/hG7IpbgzQ3k2XSirzf8/eI7OMEs81gb1VV2fQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.5.tgz#852c361247198da6756d07d9296c2b51eddb79f6" + integrity sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ== resolve-cwd@^2.0.0: version "2.0.0" @@ -12260,7 +12197,7 @@ resolve@1.19.0: is-core-module "^2.1.0" path-parse "^1.0.6" -resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1, resolve@^1.9.0: +resolve@^1.1.7, resolve@^1.3.2, resolve@^1.8.1, resolve@^1.9.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12329,7 +12266,7 @@ rework@1.0.1: convert-source-map "^0.3.3" css "^2.0.0" -rfdc@^1.1.4: +rfdc@^1.1.4, rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== @@ -12381,9 +12318,9 @@ rollup@2.38.4: fsevents "~2.3.1" rtl-css-js@^1.13.1: - version "1.14.5" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.14.5.tgz#fd92685acd024e688dda899a28c5fb9270b79974" - integrity sha512-+ng7LWVvPjQUdgDVviR6vKi2X4JiBtlw5rdY0UM5/Cj39c2/KDUsY/VxEzGE25m4KR5g0dvuKfrDq7DaoDooIA== + version "1.15.0" + resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.15.0.tgz#680ed816e570a9ebccba9e1cd0f202c6a8bb2dc0" + integrity sha512-99Cu4wNNIhrI10xxUaABHsdDqzalrSRTie4GeCmbGVuehm4oj+fIy8fTzB+16pmKe8Bv9rl+hxIBez6KxExTew== dependencies: "@babel/runtime" "^7.1.2" @@ -12593,7 +12530,7 @@ schema-utils@^2.6.0, schema-utils@^2.6.1, schema-utils@^2.6.5, schema-utils@^2.6 ajv "^6.12.4" ajv-keywords "^3.5.2" -schema-utils@^3.0.0, schema-utils@^3.1.0: +schema-utils@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.1.1.tgz#bc74c4b6b6995c1d88f76a8b77bea7219e0c8281" integrity sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw== @@ -12662,11 +12599,6 @@ semver-intersect@1.4.0: dependencies: semver "^5.0.0" -"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - semver@7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" @@ -12684,6 +12616,11 @@ semver@7.3.4: dependencies: lru-cache "^6.0.0" +semver@^5.0.0, semver@^5.0.1, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -12875,11 +12812,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.6.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.7.3.tgz#aa40edac170445b9a431e17bb62c0b881b9c4123" - integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -12890,9 +12822,9 @@ side-channel@^1.0.4: object-inspect "^1.9.0" signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: - version "3.0.5" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" - integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== + version "3.0.6" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" + integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== simple-swizzle@^0.2.2: version "0.2.2" @@ -12902,12 +12834,12 @@ simple-swizzle@^0.2.2: is-arrayish "^0.3.1" sirv@^1.0.7: - version "1.0.18" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.18.tgz#105fab52fb656ce8a2bebbf36b11052005952899" - integrity sha512-f2AOPogZmXgJ9Ma2M22ZEhc1dNtRIzcEkiflMFeVTRq+OViOZMvH1IPMVOwrKaxpSaHioBJiDR0SluRqGa7atA== + version "1.0.19" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" + integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== dependencies: "@polka/url" "^1.0.0-next.20" - mime "^2.3.1" + mrmime "^1.0.0" totalist "^1.0.0" sisteransi@^1.0.5: @@ -13071,12 +13003,12 @@ sockjs@0.3.20: websocket-driver "0.6.5" sockjs@^0.3.21: - version "0.3.21" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.21.tgz#b34ffb98e796930b60a0cfa11904d6a339a7d417" - integrity sha512-DhbPFGpxjc6Z3I+uX07Id5ZO2XwYsWOrYjaSeieES78cq+JaJvVe5q/m1uvjIQhXinhIeCFRH6JgXe+mvVMyXw== + version "0.3.24" + resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== dependencies: faye-websocket "^0.11.3" - uuid "^3.4.0" + uuid "^8.3.2" websocket-driver "^0.7.4" socks-proxy-agent@^5.0.0: @@ -13113,10 +13045,10 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +source-map-js@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== source-map-loader@1.0.2: version "1.0.2" @@ -13168,10 +13100,10 @@ source-map-support@0.5.19: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.12, source-map-support@~0.5.20: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== +source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.12, source-map-support@~0.5.19, source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" @@ -13183,14 +13115,6 @@ source-map-support@~0.4.0: dependencies: source-map "^0.5.6" -source-map-support@~0.5.19: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map-url@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" @@ -13221,32 +13145,6 @@ sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.11" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.11.tgz#50c0d8c40a14ec1bf449bae69a0ea4685a9d9f95" - integrity sha512-Ctl2BrFiM0X3MANYgj3CkygxhRmr9mi6xhejbdO960nF6EDJApTYpn0BQnDKlnNBULKiCN1n3w9EBkHK8ZWg+g== - spdy-transport@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" @@ -13301,7 +13199,7 @@ sprintf-js@~1.0.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= -sshpk@^1.7.0: +sshpk@^1.14.1, sshpk@^1.7.0: version "1.16.1" resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== @@ -13468,15 +13366,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string.prototype.padend@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.3.tgz#997a6de12c92c7cb34dc8a201a6c53d9bd88a5f1" - integrity sha512-jNIIeokznm8SD/TZISQsZKYu7RJyheFNt84DUPrh482GC8RVp2MKqm2O5oBRdGxbDQoXrhhWtPIWQOiy20svUg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -13542,11 +13431,6 @@ strip-ansi@^7.0.0: dependencies: ansi-regex "^6.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-eof@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" @@ -14490,14 +14374,6 @@ uuid@^3.0.0, uuid@^3.2.1, uuid@^3.3.2, uuid@^3.4.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - validate-npm-package-name@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" @@ -14775,9 +14651,9 @@ webpack-dev-server@3.11.2: yargs "^13.3.2" webpack-dev-server@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.5.0.tgz#614b5112cfa4730a4801bb4ddebb3be5b0d70497" - integrity sha512-Ss4WptsUjYa+3hPI4iYZYEc8FrtnfkaPrm5WTjk9ux5kiCS718836srs0ppKMHRaCHP5mQ6g4JZGcfDdGbCjpQ== + version "4.6.0" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.6.0.tgz#e8648601c440172d9b6f248d28db98bed335315a" + integrity sha512-oojcBIKvx3Ya7qs1/AVWHDgmP1Xml8rGsEBnSobxU/UJSX1xP1GPM3MwsAnDzvqcVmVki8tV7lbcsjEjk0PtYg== dependencies: ansi-html-community "^0.0.8" bonjour "^3.5.0" @@ -14785,17 +14661,17 @@ webpack-dev-server@^4.5.0: colorette "^2.0.10" compression "^1.7.4" connect-history-api-fallback "^1.6.0" + default-gateway "^6.0.3" del "^6.0.0" express "^4.17.1" graceful-fs "^4.2.6" html-entities "^2.3.2" http-proxy-middleware "^2.0.0" - internal-ip "^6.2.0" ipaddr.js "^2.0.1" open "^8.0.9" p-retry "^4.5.0" portfinder "^1.0.28" - schema-utils "^3.1.0" + schema-utils "^4.0.0" selfsigned "^1.10.11" serve-index "^1.9.1" sockjs "^0.3.21" @@ -15126,14 +15002,14 @@ ws@^6.2.1: async-limiter "~1.0.0" ws@^7.3.1, ws@^7.4.6: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== + version "7.5.6" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" + integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== ws@^8.1.0: - version "8.2.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" - integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + version "8.3.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.3.0.tgz#7185e252c8973a60d57170175ff55fdbd116070d" + integrity sha512-Gs5EZtpqZzLvmIM59w4igITU57lrtYVFneaa434VROv4thzJyV6UjIL3D42lslWlI+D4KzLYnxSwtfuiO79sNw== ws@~7.4.2: version "7.4.6"