diff --git a/projects/frontend/shared-components/gui/.editorconfig b/projects/frontend/shared-components/gui/.editorconfig
new file mode 100644
index 0000000000..5eccf58ea0
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.editorconfig
@@ -0,0 +1,23 @@
+# Editor configuration, see https://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 4
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{ts, js}]
+quote_type = single
+
+[*.js]
+max_line_length = off
+
+[*.{json, scss}]
+quote_type = double
+indent_size = 2
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/projects/frontend/shared-components/gui/.eslintignore b/projects/frontend/shared-components/gui/.eslintignore
new file mode 100644
index 0000000000..91bbe59907
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.eslintignore
@@ -0,0 +1,4 @@
+projects/shared/src/main.ts
+projects/documentation-ui/src/main.ts
+projects/documentation-ui/src/test.ts
+polyfills.ts
diff --git a/projects/frontend/shared-components/gui/.eslintrc.json b/projects/frontend/shared-components/gui/.eslintrc.json
new file mode 100644
index 0000000000..9cf9e07c32
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.eslintrc.json
@@ -0,0 +1,108 @@
+{
+ "root": true,
+ "ignorePatterns": [
+ "projects/**/*",
+ "**/node_modules/*"
+ ],
+ "settings": {
+ "import/parsers": {
+ "@typescript-eslint/parser": [
+ ".ts",
+ ".tsx"
+ ]
+ },
+ "import/resolver": {
+ "typescript": {
+ "project": [
+ "tsconfig.json"
+ ]
+ },
+ "node": {
+ "project": [
+ "tsconfig.json"
+ ]
+ }
+ }
+ },
+ "overrides": [
+ {
+ "files": [
+ "*.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "tsconfig.json"
+ ],
+ "createDefaultProgram": true
+ },
+ "extends": [
+ "plugin:@typescript-eslint/recommended",
+ "plugin:@typescript-eslint/recommended-requiring-type-checking",
+ "plugin:@angular-eslint/recommended",
+ "plugin:@angular-eslint/template/process-inline-templates",
+ "plugin:import/recommended",
+ "plugin:import/typescript",
+ "plugin:ngrx/recommended",
+ "plugin:rxjs/recommended"
+ ],
+ "plugins": [
+ "ngrx",
+ "prefer-arrow",
+ "rxjs"
+ ],
+ "rules": {
+ "@typescript-eslint/no-explicit-any": "warn",
+ "@typescript-eslint/no-unsafe-assignment": "warn",
+ "@typescript-eslint/dot-notation": [
+ "warn",
+ {
+ "allowPrivateClassPropertyAccess": true,
+ "allowProtectedClassPropertyAccess": true,
+ "allowIndexSignaturePropertyAccess": true
+ }
+ ],
+ "@typescript-eslint/unbound-method": "warn",
+ "@typescript-eslint/no-floating-promises": "warn",
+ "@typescript-eslint/ban-ts-comment": "warn",
+ "@typescript-eslint/no-unsafe-argument": "error",
+ "@typescript-eslint/no-unused-vars": [
+ "warn",
+ {
+ "argsIgnorePattern": "^_"
+ }
+ ],
+ "ngrx/prefer-effect-callback-in-block-statement": "off",
+ "ngrx/use-consistent-global-store-name": "off",
+ "ngrx/prefer-action-creator": "off",
+ "ngrx/no-typed-global-store": "off",
+ "ngrx/prefer-selector-in-select": "off",
+ "no-underscore-dangle": "off"
+ }
+ },
+ {
+ "files": [
+ "*.html"
+ ],
+ "extends": [
+ "plugin:@angular-eslint/template/recommended"
+ ],
+ "rules": {
+ "@angular-eslint/template/conditional-complexity": [
+ "warn",
+ {
+ "maxComplexity": 10
+ }
+ ],
+ "@angular-eslint/template/cyclomatic-complexity": [
+ "warn",
+ {
+ "maxComplexity": 10
+ }
+ ],
+ "@angular-eslint/template/no-positive-tabindex": "warn",
+ "@angular-eslint/template/use-track-by-function": "off",
+ "@angular-eslint/template/no-call-expression": "warn"
+ }
+ }
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/.gitignore b/projects/frontend/shared-components/gui/.gitignore
index 53a8849eea..fc257e9a4f 100644
--- a/projects/frontend/shared-components/gui/.gitignore
+++ b/projects/frontend/shared-components/gui/.gitignore
@@ -41,6 +41,7 @@ npm-debug.log
yarn-error.log
testem.log
/typings
+/reports/**
# System Files
.DS_Store
diff --git a/projects/frontend/shared-components/gui/.npmrc b/projects/frontend/shared-components/gui/.npmrc
new file mode 100644
index 0000000000..521a9f7c07
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.npmrc
@@ -0,0 +1 @@
+legacy-peer-deps=true
diff --git a/projects/frontend/shared-components/gui/.nvmrc b/projects/frontend/shared-components/gui/.nvmrc
new file mode 100644
index 0000000000..99cdd8009c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.nvmrc
@@ -0,0 +1 @@
+16.15.0
diff --git a/projects/frontend/shared-components/gui/.prettierrc.json b/projects/frontend/shared-components/gui/.prettierrc.json
new file mode 100644
index 0000000000..b09315e4c1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/.prettierrc.json
@@ -0,0 +1,26 @@
+{
+ "tabWidth": 2,
+ "useTabs": true,
+ "semi": true,
+ "singleQuote": true,
+ "quoteProps": "as-needed",
+ "trailingComma": "none",
+ "bracketSpacing": true,
+ "jsxBracketSameLine": true,
+ "arrowParens": "always",
+ "insertPragma": false,
+ "overrides": [
+ {
+ "files": "*.component.html",
+ "options": {
+ "parser": "angular"
+ }
+ },
+ {
+ "files": "*.html",
+ "options": {
+ "parser": "html"
+ }
+ }
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/README.md b/projects/frontend/shared-components/gui/README.md
index 2aa2c0e0f6..31d993013d 100644
--- a/projects/frontend/shared-components/gui/README.md
+++ b/projects/frontend/shared-components/gui/README.md
@@ -22,7 +22,7 @@ Bug in general the steps are:
```bash
$ npm config set legacy-peer-deps true
```
-1. Install Angular Cli and all other dependencies except '@taurus/shared'
+1. Install Angular Cli and all other dependencies except '@vdk/shared'
```bash
npm i -g @angular/cli
```
@@ -64,6 +64,3 @@ Run `npm run lint` to execute lint via [ESLint](https://eslint.org/docs/user-gui
_**Note**_: Code coverage report will be generated in `reports/coverage`(for the UI Document Wrapper and the Shared Components Library)
_**Note**_: Code coverage report for the Shared UI Components library will be also logged in the Console(and consumed by the CI/CD)
-
-[E2E] Run `npm run e2e` to open [Cypress](https://www.cypress.io/) dev tool for e2e testing
-- Cypress environment variables must be supplied providing credentials for needed infrastructure
diff --git a/projects/frontend/shared-components/gui/angular.json b/projects/frontend/shared-components/gui/angular.json
new file mode 100644
index 0000000000..226088c167
--- /dev/null
+++ b/projects/frontend/shared-components/gui/angular.json
@@ -0,0 +1,214 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "cli": {
+ "analytics": false,
+ "defaultCollection": "@angular-eslint/schematics",
+ "packageManager": "npm"
+ },
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "shared": {
+ "projectType": "library",
+ "root": "projects/shared",
+ "sourceRoot": "projects/shared/src",
+ "prefix": "shared",
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:ng-packagr",
+ "options": {
+ "tsConfig": "projects/shared/tsconfig.lib.json",
+ "project": "projects/shared/ng-package.json"
+ },
+ "configurations": {
+ "production": {
+ "tsConfig": "projects/shared/tsconfig.lib.prod.json",
+ "project": "projects/shared/ng-package.json"
+ },
+ "development": {}
+ },
+ "defaultConfiguration": "production"
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "projects/shared/src/test.ts",
+ "tsConfig": "projects/shared/tsconfig.spec.json",
+ "karmaConfig": "projects/shared/karma.conf.js",
+ "codeCoverage": true,
+ "codeCoverageExclude": [
+ "projects/shared/src/**/*component.ts",
+ "projects/shared/src/lib/unit-testing/**/*"
+ ],
+ "sourceMap": {
+ "scripts": true,
+ "vendor": true,
+ "styles": true
+ }
+ }
+ },
+ "lint": {
+ "builder": "@angular-eslint/builder:lint",
+ "options": {
+ "lintFilePatterns": [
+ "projects/shared/**/*.ts",
+ "projects/shared/**/*.html"
+ ]
+ }
+ }
+ },
+ "schematics": {
+ "@schematics/angular:component": {
+ "style": "scss"
+ }
+ }
+ },
+ "documentation-ui": {
+ "projectType": "application",
+ "root": "projects/documentation-ui",
+ "sourceRoot": "projects/documentation-ui/src",
+ "prefix": "app",
+ "architect": {
+ "build": {
+ "builder": "@angular-devkit/build-angular:browser",
+ "options": {
+ "outputPath": "dist/documentation-ui",
+ "index": "projects/documentation-ui/src/index.html",
+ "main": "projects/documentation-ui/src/main.ts",
+ "polyfills": "projects/documentation-ui/src/polyfills.ts",
+ "tsConfig": "projects/documentation-ui/tsconfig.app.json",
+ "allowedCommonJsDependencies": [
+ "dagre",
+ "webcola"
+ ],
+ "assets": [
+ "projects/documentation-ui/src/favicon.ico",
+ "projects/documentation-ui/src/assets",
+ {
+ "glob": "clr-ui*.min.*",
+ "input": "node_modules/@clr/ui",
+ "output": "assets/css"
+ },
+ {
+ "glob": "**/*",
+ "input": "node_modules/@vdk/shared/assets",
+ "output": "/assets/"
+ }
+ ],
+ "styles": [
+ "node_modules/@clr/icons/clr-icons.min.css",
+ "projects/documentation-ui/src/styles.scss"
+ ],
+ "scripts": [
+ "node_modules/@clr/icons/clr-icons.min.js"
+ ]
+ },
+ "configurations": {
+ "development": {
+ "sourceMap": {
+ "scripts": true,
+ "vendor": true,
+ "styles": true
+ },
+ "vendorChunk": true,
+ "extractLicenses": false,
+ "buildOptimizer": false,
+ "optimization": false,
+ "namedChunks": true,
+ "preserveSymlinks": true
+ },
+ "production": {
+ "fileReplacements": [
+ {
+ "replace": "projects/documentation-ui/src/environments/environment.ts",
+ "with": "projects/documentation-ui/src/environments/environment.prod.ts"
+ }
+ ],
+ "sourceMap": false,
+ "vendorChunk": false,
+ "extractLicenses": true,
+ "buildOptimizer": true,
+ "optimization": true,
+ "namedChunks": false,
+ "outputHashing": "all",
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "2mb",
+ "maximumError": "5mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "6kb",
+ "maximumError": "10kb"
+ }
+ ]
+ }
+ },
+ "defaultConfiguration": "development"
+ },
+ "serve": {
+ "builder": "@angular-devkit/build-angular:dev-server",
+ "options": {
+ "host": "localhost.vmware.com",
+ "port": 4200,
+ "proxyConfig": "proxy.config.json"
+ },
+ "configurations": {
+ "development": {
+ "browserTarget": "documentation-ui:build:development"
+ },
+ "production": {
+ "browserTarget": "documentation-ui:build:production"
+ }
+ },
+ "defaultConfiguration": "development"
+ },
+ "extract-i18n": {
+ "builder": "@angular-devkit/build-angular:extract-i18n",
+ "options": {
+ "browserTarget": "documentation-ui:build"
+ }
+ },
+ "test": {
+ "builder": "@angular-devkit/build-angular:karma",
+ "options": {
+ "main": "projects/documentation-ui/src/test.ts",
+ "polyfills": "projects/documentation-ui/src/polyfills.ts",
+ "tsConfig": "projects/documentation-ui/tsconfig.spec.json",
+ "karmaConfig": "projects/documentation-ui/karma.conf.js",
+ "codeCoverage": true,
+ "codeCoverageExclude": [
+ "projects/documentation-ui/src/**/*component.ts"
+ ],
+ "assets": [
+ "projects/documentation-ui/src/favicon.ico",
+ "projects/documentation-ui/src/assets"
+ ],
+ "styles": [
+ "projects/documentation-ui/src/styles.scss"
+ ],
+ "scripts": [
+ "node_modules/@clr/icons/clr-icons.min.js"
+ ]
+ }
+ },
+ "lint": {
+ "builder": "@angular-eslint/builder:lint",
+ "options": {
+ "lintFilePatterns": [
+ "projects/documentation-ui/**/*.ts",
+ "projects/documentation-ui/**/*.html"
+ ]
+ }
+ }
+ },
+ "schematics": {
+ "@schematics/angular:component": {
+ "style": "scss"
+ }
+ }
+ }
+ },
+ "defaultProject": "shared"
+}
diff --git a/projects/frontend/shared-components/gui/package-lock.json b/projects/frontend/shared-components/gui/package-lock.json
new file mode 100644
index 0000000000..803ce18d13
--- /dev/null
+++ b/projects/frontend/shared-components/gui/package-lock.json
@@ -0,0 +1,16476 @@
+{
+ "name": "shared-wrapper",
+ "version": "3.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "shared-wrapper",
+ "version": "3.0.0",
+ "dependencies": {
+ "@angular/animations": "13.3.10",
+ "@angular/cdk": "13.3.8",
+ "@angular/common": "13.3.10",
+ "@angular/compiler": "13.3.10",
+ "@angular/core": "13.3.10",
+ "@angular/forms": "13.3.10",
+ "@angular/platform-browser": "13.3.10",
+ "@angular/platform-browser-dynamic": "13.3.10",
+ "@angular/router": "13.3.10",
+ "@cds/core": "5.7.2",
+ "@clr/angular": "13.3.1",
+ "@clr/icons": "13.0.2",
+ "@clr/ui": "13.3.1",
+ "@ngrx/effects": "13.2.0",
+ "@ngrx/router-store": "13.2.0",
+ "@ngrx/store": "13.2.0",
+ "@swimlane/ngx-graph": "8.0.0",
+ "@webcomponents/custom-elements": "1.5.0",
+ "angular-ng-autocomplete": "2.0.8",
+ "d3": "5.16.0",
+ "dompurify": "2.3.8",
+ "lodash": "4.17.21",
+ "ngx-clipboard": "15.1.0",
+ "ngx-cookie-service": "13.2.1",
+ "rxjs": "7.5.5",
+ "tslib": "2.3.1",
+ "zone.js": "0.11.7"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "13.3.7",
+ "@angular-devkit/schematics": "13.3.7",
+ "@angular-eslint/builder": "13.2.1",
+ "@angular-eslint/eslint-plugin": "13.2.1",
+ "@angular-eslint/eslint-plugin-template": "13.2.1",
+ "@angular-eslint/schematics": "13.2.1",
+ "@angular-eslint/template-parser": "13.2.1",
+ "@angular/cli": "13.3.7",
+ "@angular/compiler-cli": "13.3.10",
+ "@neuralegion/cypress-har-generator": "5.3.0",
+ "@ngrx/schematics": "13.2.0",
+ "@ngrx/store-devtools": "13.2.0",
+ "@types/jasmine": "4.0.3",
+ "@types/lodash": "4.14.182",
+ "@types/node": "17.0.35",
+ "@typescript-eslint/eslint-plugin": "5.26.0",
+ "@typescript-eslint/parser": "5.26.0",
+ "angular-oauth2-oidc": "13.0.1",
+ "cypress": "9.7.0",
+ "cypress-grep": "2.13.1",
+ "cypress-localstorage-commands": "1.7.0",
+ "cypress-terminal-report": "3.5.2",
+ "eslint": "8.16.0",
+ "eslint-import-resolver-typescript": "2.7.1",
+ "eslint-plugin-cypress": "2.12.1",
+ "eslint-plugin-import": "2.26.0",
+ "eslint-plugin-jasmine": "4.1.3",
+ "eslint-plugin-jsdoc": "39.3.2",
+ "eslint-plugin-ngrx": "2.1.2",
+ "eslint-plugin-prefer-arrow": "1.2.3",
+ "eslint-plugin-prettier": "4.0.0",
+ "eslint-plugin-rxjs": "5.0.2",
+ "jasmine-core": "4.1.1",
+ "jasmine-spec-reporter": "7.0.0",
+ "karma": "6.3.20",
+ "karma-chrome-launcher": "3.1.1",
+ "karma-coverage": "2.2.0",
+ "karma-jasmine": "5.0.1",
+ "karma-jasmine-html-reporter": "1.7.0",
+ "karma-junit-reporter": "2.0.1",
+ "ng-packagr": "13.3.1",
+ "npm-run-all": "4.1.5",
+ "prettier": "2.6.2",
+ "rimraf": "3.0.2",
+ "rxjs-marbles": "7.0.1",
+ "start-server-and-test": "1.14.0",
+ "typescript": "4.6.4"
+ },
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": ">=8.5.5"
+ },
+ "optionalDependencies": {
+ "@vdk/shared": "0.0.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.1.0",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect": {
+ "version": "0.1303.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "13.3.7",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@angular-devkit/build-angular": {
+ "version": "13.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "2.2.0",
+ "@angular-devkit/architect": "0.1303.7",
+ "@angular-devkit/build-webpack": "0.1303.7",
+ "@angular-devkit/core": "13.3.7",
+ "@babel/core": "7.16.12",
+ "@babel/generator": "7.16.8",
+ "@babel/helper-annotate-as-pure": "7.16.7",
+ "@babel/plugin-proposal-async-generator-functions": "7.16.8",
+ "@babel/plugin-transform-async-to-generator": "7.16.8",
+ "@babel/plugin-transform-runtime": "7.16.10",
+ "@babel/preset-env": "7.16.11",
+ "@babel/runtime": "7.16.7",
+ "@babel/template": "7.16.7",
+ "@discoveryjs/json-ext": "0.5.6",
+ "@ngtools/webpack": "13.3.7",
+ "ansi-colors": "4.1.1",
+ "babel-loader": "8.2.5",
+ "babel-plugin-istanbul": "6.1.1",
+ "browserslist": "^4.9.1",
+ "cacache": "15.3.0",
+ "circular-dependency-plugin": "5.2.2",
+ "copy-webpack-plugin": "10.2.1",
+ "core-js": "3.20.3",
+ "critters": "0.0.16",
+ "css-loader": "6.5.1",
+ "esbuild-wasm": "0.14.22",
+ "glob": "7.2.0",
+ "https-proxy-agent": "5.0.0",
+ "inquirer": "8.2.0",
+ "jsonc-parser": "3.0.0",
+ "karma-source-map-support": "1.4.0",
+ "less": "4.1.2",
+ "less-loader": "10.2.0",
+ "license-webpack-plugin": "4.0.2",
+ "loader-utils": "3.2.0",
+ "mini-css-extract-plugin": "2.5.3",
+ "minimatch": "3.0.5",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "parse5-html-rewriting-stream": "6.0.1",
+ "piscina": "3.2.0",
+ "postcss": "8.4.5",
+ "postcss-import": "14.0.2",
+ "postcss-loader": "6.2.1",
+ "postcss-preset-env": "7.2.3",
+ "regenerator-runtime": "0.13.9",
+ "resolve-url-loader": "5.0.0",
+ "rxjs": "6.6.7",
+ "sass": "1.49.9",
+ "sass-loader": "12.4.0",
+ "semver": "7.3.5",
+ "source-map-loader": "3.0.1",
+ "source-map-support": "0.5.21",
+ "stylus": "0.56.0",
+ "stylus-loader": "6.2.0",
+ "terser": "5.11.0",
+ "text-table": "0.2.0",
+ "tree-kill": "1.2.2",
+ "tslib": "2.3.1",
+ "webpack": "5.70.0",
+ "webpack-dev-middleware": "5.3.0",
+ "webpack-dev-server": "4.7.3",
+ "webpack-merge": "5.8.0",
+ "webpack-subresource-integrity": "5.1.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "optionalDependencies": {
+ "esbuild": "0.14.22"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^13.0.0 || ^13.3.0-rc.0",
+ "@angular/localize": "^13.0.0 || ^13.3.0-rc.0",
+ "@angular/service-worker": "^13.0.0 || ^13.3.0-rc.0",
+ "karma": "^6.3.0",
+ "ng-packagr": "^13.0.0",
+ "protractor": "^7.0.0",
+ "tailwindcss": "^2.0.0 || ^3.0.0",
+ "typescript": ">=4.4.3 <4.7"
+ },
+ "peerDependenciesMeta": {
+ "@angular/localize": {
+ "optional": true
+ },
+ "@angular/service-worker": {
+ "optional": true
+ },
+ "karma": {
+ "optional": true
+ },
+ "ng-packagr": {
+ "optional": true
+ },
+ "protractor": {
+ "optional": true
+ },
+ "tailwindcss": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@angular-devkit/build-webpack": {
+ "version": "0.1303.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/architect": "0.1303.7",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "webpack": "^5.30.0",
+ "webpack-dev-server": "^4.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/build-webpack/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@angular-devkit/core": {
+ "version": "13.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "8.9.0",
+ "ajv-formats": "2.1.1",
+ "fast-json-stable-stringify": "2.1.0",
+ "magic-string": "0.25.7",
+ "rxjs": "6.6.7",
+ "source-map": "0.7.3"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^3.5.2"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/core/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/core/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@angular-devkit/schematics": {
+ "version": "13.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "13.3.7",
+ "jsonc-parser": "3.0.0",
+ "magic-string": "0.25.7",
+ "ora": "5.4.1",
+ "rxjs": "6.6.7"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/schematics/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@angular-eslint/builder": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nrwl/devkit": "13.1.3"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0",
+ "typescript": "*"
+ }
+ },
+ "node_modules/@angular-eslint/bundled-angular-compiler": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@angular-eslint/eslint-plugin": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-eslint/utils": "13.2.1",
+ "@typescript-eslint/experimental-utils": "5.17.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0",
+ "typescript": "*"
+ }
+ },
+ "node_modules/@angular-eslint/eslint-plugin-template": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-eslint/bundled-angular-compiler": "13.2.1",
+ "@typescript-eslint/experimental-utils": "5.17.0",
+ "aria-query": "^4.2.2",
+ "axobject-query": "^2.2.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0",
+ "typescript": "*"
+ }
+ },
+ "node_modules/@angular-eslint/schematics": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-eslint/eslint-plugin": "13.2.1",
+ "@angular-eslint/eslint-plugin-template": "13.2.1",
+ "ignore": "5.2.0",
+ "strip-json-comments": "3.1.1",
+ "tmp": "0.2.1"
+ },
+ "peerDependencies": {
+ "@angular/cli": ">= 13.0.0 < 14.0.0"
+ }
+ },
+ "node_modules/@angular-eslint/template-parser": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-eslint/bundled-angular-compiler": "13.2.1",
+ "eslint-scope": "^5.1.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0",
+ "typescript": "*"
+ }
+ },
+ "node_modules/@angular-eslint/utils": {
+ "version": "13.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-eslint/bundled-angular-compiler": "13.2.1",
+ "@typescript-eslint/experimental-utils": "5.17.0"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0",
+ "typescript": "*"
+ }
+ },
+ "node_modules/@angular/animations": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "13.3.10"
+ }
+ },
+ "node_modules/@angular/cdk": {
+ "version": "13.3.8",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "optionalDependencies": {
+ "parse5": "^5.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^13.0.0 || ^14.0.0-0",
+ "@angular/core": "^13.0.0 || ^14.0.0-0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/cli": {
+ "version": "13.3.7",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/architect": "0.1303.7",
+ "@angular-devkit/core": "13.3.7",
+ "@angular-devkit/schematics": "13.3.7",
+ "@schematics/angular": "13.3.7",
+ "@yarnpkg/lockfile": "1.1.0",
+ "ansi-colors": "4.1.1",
+ "debug": "4.3.3",
+ "ini": "2.0.0",
+ "inquirer": "8.2.0",
+ "jsonc-parser": "3.0.0",
+ "npm-package-arg": "8.1.5",
+ "npm-pick-manifest": "6.1.1",
+ "open": "8.4.0",
+ "ora": "5.4.1",
+ "pacote": "12.0.3",
+ "resolve": "1.22.0",
+ "semver": "7.3.5",
+ "symbol-observable": "4.0.0",
+ "uuid": "8.3.2"
+ },
+ "bin": {
+ "ng": "bin/ng.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular/common": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "13.3.10",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/compiler": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ }
+ },
+ "node_modules/@angular/compiler-cli": {
+ "version": "13.3.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.17.2",
+ "chokidar": "^3.0.0",
+ "convert-source-map": "^1.5.1",
+ "dependency-graph": "^0.11.0",
+ "magic-string": "^0.26.0",
+ "reflect-metadata": "^0.1.2",
+ "semver": "^7.0.0",
+ "sourcemap-codec": "^1.4.8",
+ "tslib": "^2.3.0",
+ "yargs": "^17.2.1"
+ },
+ "bin": {
+ "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
+ "ngc": "bundles/src/bin/ngc.js",
+ "ngcc": "bundles/ngcc/main-ngcc.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "13.3.10",
+ "typescript": ">=4.4.2 <4.7"
+ }
+ },
+ "node_modules/@angular/compiler-cli/node_modules/@babel/core": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.1.0",
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.18.2",
+ "@babel/helper-compilation-targets": "^7.18.2",
+ "@babel/helper-module-transforms": "^7.18.0",
+ "@babel/helpers": "^7.18.2",
+ "@babel/parser": "^7.18.0",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.18.2",
+ "@babel/types": "^7.18.2",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.1",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@angular/compiler-cli/node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@angular/compiler-cli/node_modules/@babel/generator": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.18.2",
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@angular/compiler-cli/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@angular/compiler-cli/node_modules/magic-string": {
+ "version": "0.26.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sourcemap-codec": "^1.4.8"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@angular/core": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "rxjs": "^6.5.3 || ^7.4.0",
+ "zone.js": "~0.11.4"
+ }
+ },
+ "node_modules/@angular/forms": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "13.3.10",
+ "@angular/core": "13.3.10",
+ "@angular/platform-browser": "13.3.10",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/platform-browser": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/animations": "13.3.10",
+ "@angular/common": "13.3.10",
+ "@angular/core": "13.3.10"
+ },
+ "peerDependenciesMeta": {
+ "@angular/animations": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/platform-browser-dynamic": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "13.3.10",
+ "@angular/compiler": "13.3.10",
+ "@angular/core": "13.3.10",
+ "@angular/platform-browser": "13.3.10"
+ }
+ },
+ "node_modules/@angular/router": {
+ "version": "13.3.10",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "13.3.10",
+ "@angular/core": "13.3.10",
+ "@angular/platform-browser": "13.3.10",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@assemblyscript/loader": {
+ "version": "0.10.1",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/highlight": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.17.10",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.16.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.16.8",
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-module-transforms": "^7.16.7",
+ "@babel/helpers": "^7.16.7",
+ "@babel/parser": "^7.16.12",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.16.10",
+ "@babel/types": "^7.16.8",
+ "convert-source-map": "^1.7.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.1.2",
+ "semver": "^6.3.0",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/core/node_modules/source-map": {
+ "version": "0.5.7",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.16.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.8",
+ "jsesc": "^2.5.1",
+ "source-map": "^0.5.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/generator/node_modules/source-map": {
+ "version": "0.5.7",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-explode-assignable-expression": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.17.10",
+ "@babel/helper-validator-option": "^7.16.7",
+ "browserslist": "^4.20.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.17.9",
+ "@babel/helper-member-expression-to-functions": "^7.17.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "regexpu-core": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.13.0",
+ "@babel/helper-module-imports": "^7.12.13",
+ "@babel/helper-plugin-utils": "^7.13.0",
+ "@babel/traverse": "^7.13.0",
+ "debug": "^4.1.1",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.14.2",
+ "semver": "^6.1.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0-0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-environment-visitor": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-explode-assignable-expression": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-function-name": {
+ "version": "7.17.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.16.7",
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-hoist-variables": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.17.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.17.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-simple-access": "^7.17.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.18.0",
+ "@babel/types": "^7.18.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.16.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-wrap-function": "^7.16.8",
+ "@babel/types": "^7.16.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-environment-visitor": "^7.18.2",
+ "@babel/helper-member-expression-to-functions": "^7.17.7",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/traverse": "^7.18.2",
+ "@babel/types": "^7.18.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-simple-access": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.18.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.16.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.16.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.16.8",
+ "@babel/types": "^7.16.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.16.7",
+ "@babel/traverse": "^7.18.2",
+ "@babel/types": "^7.18.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/highlight": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "chalk": "^2.0.0",
+ "js-tokens": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.18.3",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
+ "@babel/plugin-proposal-optional-chaining": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-async-generator-functions": {
+ "version": "7.16.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-remap-async-to-generator": "^7.16.8",
+ "@babel/plugin-syntax-async-generators": "^7.8.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-class-properties": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.17.12",
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-class-static-block": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.18.0",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-dynamic-import": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-export-namespace-from": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-json-strings": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-json-strings": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-logical-assignment-operators": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-nullish-coalescing-operator": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-numeric-separator": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-object-rest-spread": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.17.10",
+ "@babel/helper-compilation-targets": "^7.17.10",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-transform-parameters": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-optional-catch-binding": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-optional-chaining": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-methods": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.17.12",
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-create-class-features-plugin": "^7.17.12",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-unicode-property-regex": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.17.12",
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-async-generators": {
+ "version": "7.8.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-properties": {
+ "version": "7.12.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.12.13"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-class-static-block": {
+ "version": "7.14.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-dynamic-import": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-export-namespace-from": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.3"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-json-strings": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-logical-assignment-operators": {
+ "version": "7.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-numeric-separator": {
+ "version": "7.10.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-object-rest-spread": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-catch-binding": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-optional-chaining": {
+ "version": "7.8.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.8.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-private-property-in-object": {
+ "version": "7.14.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-top-level-await": {
+ "version": "7.14.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.14.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.16.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-remap-async-to-generator": "^7.16.8"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.16.7",
+ "@babel/helper-environment-visitor": "^7.16.7",
+ "@babel/helper-function-name": "^7.17.9",
+ "@babel/helper-optimise-call-expression": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-replace-supers": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-builder-binary-assignment-operator-visitor": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.18.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-function-name": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.18.0",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.18.0",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-simple-access": "^7.18.2",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-module-transforms": "^7.18.0",
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "babel-plugin-dynamic-import-node": "^2.3.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.18.0",
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.17.12",
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-replace-supers": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "regenerator-transform": "^0.15.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-runtime": {
+ "version": "7.16.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "babel-plugin-polyfill-corejs2": "^0.3.0",
+ "babel-plugin-polyfill-corejs3": "^0.5.0",
+ "babel-plugin-polyfill-regenerator": "^0.3.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-runtime/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.16.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.17.12",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.17.12"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.16.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.16.8",
+ "@babel/helper-compilation-targets": "^7.16.7",
+ "@babel/helper-plugin-utils": "^7.16.7",
+ "@babel/helper-validator-option": "^7.16.7",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.16.7",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.16.7",
+ "@babel/plugin-proposal-async-generator-functions": "^7.16.8",
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
+ "@babel/plugin-proposal-class-static-block": "^7.16.7",
+ "@babel/plugin-proposal-dynamic-import": "^7.16.7",
+ "@babel/plugin-proposal-export-namespace-from": "^7.16.7",
+ "@babel/plugin-proposal-json-strings": "^7.16.7",
+ "@babel/plugin-proposal-logical-assignment-operators": "^7.16.7",
+ "@babel/plugin-proposal-nullish-coalescing-operator": "^7.16.7",
+ "@babel/plugin-proposal-numeric-separator": "^7.16.7",
+ "@babel/plugin-proposal-object-rest-spread": "^7.16.7",
+ "@babel/plugin-proposal-optional-catch-binding": "^7.16.7",
+ "@babel/plugin-proposal-optional-chaining": "^7.16.7",
+ "@babel/plugin-proposal-private-methods": "^7.16.11",
+ "@babel/plugin-proposal-private-property-in-object": "^7.16.7",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.16.7",
+ "@babel/plugin-syntax-async-generators": "^7.8.4",
+ "@babel/plugin-syntax-class-properties": "^7.12.13",
+ "@babel/plugin-syntax-class-static-block": "^7.14.5",
+ "@babel/plugin-syntax-dynamic-import": "^7.8.3",
+ "@babel/plugin-syntax-export-namespace-from": "^7.8.3",
+ "@babel/plugin-syntax-json-strings": "^7.8.3",
+ "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4",
+ "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+ "@babel/plugin-syntax-numeric-separator": "^7.10.4",
+ "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+ "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+ "@babel/plugin-syntax-optional-chaining": "^7.8.3",
+ "@babel/plugin-syntax-private-property-in-object": "^7.14.5",
+ "@babel/plugin-syntax-top-level-await": "^7.14.5",
+ "@babel/plugin-transform-arrow-functions": "^7.16.7",
+ "@babel/plugin-transform-async-to-generator": "^7.16.8",
+ "@babel/plugin-transform-block-scoped-functions": "^7.16.7",
+ "@babel/plugin-transform-block-scoping": "^7.16.7",
+ "@babel/plugin-transform-classes": "^7.16.7",
+ "@babel/plugin-transform-computed-properties": "^7.16.7",
+ "@babel/plugin-transform-destructuring": "^7.16.7",
+ "@babel/plugin-transform-dotall-regex": "^7.16.7",
+ "@babel/plugin-transform-duplicate-keys": "^7.16.7",
+ "@babel/plugin-transform-exponentiation-operator": "^7.16.7",
+ "@babel/plugin-transform-for-of": "^7.16.7",
+ "@babel/plugin-transform-function-name": "^7.16.7",
+ "@babel/plugin-transform-literals": "^7.16.7",
+ "@babel/plugin-transform-member-expression-literals": "^7.16.7",
+ "@babel/plugin-transform-modules-amd": "^7.16.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.16.8",
+ "@babel/plugin-transform-modules-systemjs": "^7.16.7",
+ "@babel/plugin-transform-modules-umd": "^7.16.7",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.16.8",
+ "@babel/plugin-transform-new-target": "^7.16.7",
+ "@babel/plugin-transform-object-super": "^7.16.7",
+ "@babel/plugin-transform-parameters": "^7.16.7",
+ "@babel/plugin-transform-property-literals": "^7.16.7",
+ "@babel/plugin-transform-regenerator": "^7.16.7",
+ "@babel/plugin-transform-reserved-words": "^7.16.7",
+ "@babel/plugin-transform-shorthand-properties": "^7.16.7",
+ "@babel/plugin-transform-spread": "^7.16.7",
+ "@babel/plugin-transform-sticky-regex": "^7.16.7",
+ "@babel/plugin-transform-template-literals": "^7.16.7",
+ "@babel/plugin-transform-typeof-symbol": "^7.16.7",
+ "@babel/plugin-transform-unicode-escapes": "^7.16.7",
+ "@babel/plugin-transform-unicode-regex": "^7.16.7",
+ "@babel/preset-modules": "^0.1.5",
+ "@babel/types": "^7.16.8",
+ "babel-plugin-polyfill-corejs2": "^0.3.0",
+ "babel-plugin-polyfill-corejs3": "^0.5.0",
+ "babel-plugin-polyfill-regenerator": "^0.3.0",
+ "core-js-compat": "^3.20.2",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-env/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/plugin-proposal-unicode-property-regex": "^7.4.4",
+ "@babel/plugin-transform-dotall-regex": "^7.4.4",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerator-runtime": "^0.13.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/runtime-corejs3": {
+ "version": "7.18.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-js-pure": "^3.20.2",
+ "regenerator-runtime": "^0.13.4"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.16.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/parser": "^7.16.7",
+ "@babel/types": "^7.16.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.16.7",
+ "@babel/generator": "^7.18.2",
+ "@babel/helper-environment-visitor": "^7.18.2",
+ "@babel/helper-function-name": "^7.17.9",
+ "@babel/helper-hoist-variables": "^7.16.7",
+ "@babel/helper-split-export-declaration": "^7.16.7",
+ "@babel/parser": "^7.18.0",
+ "@babel/types": "^7.18.2",
+ "debug": "^4.1.0",
+ "globals": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/@babel/generator": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.18.2",
+ "@jridgewell/gen-mapping": "^0.3.0",
+ "jsesc": "^2.5.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.9"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.18.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.16.7",
+ "to-fast-properties": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@cds/city": {
+ "version": "1.1.0",
+ "license": "OFL-1.1",
+ "optional": true
+ },
+ "node_modules/@cds/core": {
+ "version": "5.7.2",
+ "license": "MIT",
+ "dependencies": {
+ "lit": "^2.1.3",
+ "ramda": "^0.28.0",
+ "tslib": "^2.3.1"
+ },
+ "optionalDependencies": {
+ "@cds/city": "^1.1.0",
+ "@types/resize-observer-browser": "^0.1.5",
+ "modern-normalize": "1.1.0"
+ }
+ },
+ "node_modules/@clr/angular": {
+ "version": "13.3.1",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^13.0.0",
+ "@angular/core": "^13.0.0",
+ "@cds/core": "next || ^6.0.0 || ^5.6.0"
+ }
+ },
+ "node_modules/@clr/icons": {
+ "version": "13.0.2",
+ "license": "MIT",
+ "peerDependencies": {
+ "@webcomponents/custom-elements": "^1.0.0"
+ }
+ },
+ "node_modules/@clr/ui": {
+ "version": "13.3.1",
+ "license": "MIT",
+ "peerDependencies": {
+ "@clr/angular": "13.3.1"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@csstools/postcss-progressive-custom-properties": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
+ "node_modules/@csstools/selector-specificity": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3",
+ "postcss-selector-parser": "^6.0.10"
+ }
+ },
+ "node_modules/@cypress/request": {
+ "version": "2.88.10",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "aws-sign2": "~0.7.0",
+ "aws4": "^1.8.0",
+ "caseless": "~0.12.0",
+ "combined-stream": "~1.0.6",
+ "extend": "~3.0.2",
+ "forever-agent": "~0.6.1",
+ "form-data": "~2.3.2",
+ "http-signature": "~1.3.6",
+ "is-typedarray": "~1.0.0",
+ "isstream": "~0.1.2",
+ "json-stringify-safe": "~5.0.1",
+ "mime-types": "~2.1.19",
+ "performance-now": "^2.1.0",
+ "qs": "~6.5.2",
+ "safe-buffer": "^5.1.2",
+ "tough-cookie": "~2.5.0",
+ "tunnel-agent": "^0.6.0",
+ "uuid": "^8.3.2"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@cypress/xvfb": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.1.0",
+ "lodash.once": "^4.1.1"
+ }
+ },
+ "node_modules/@cypress/xvfb/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/@discoveryjs/json-ext": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/@es-joy/jsdoccomment": {
+ "version": "0.31.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "comment-parser": "1.3.1",
+ "esquery": "^1.4.0",
+ "jsdoc-type-pratt-parser": "~3.1.0"
+ },
+ "engines": {
+ "node": "^14 || ^16 || ^17 || ^18"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.3.2",
+ "globals": "^13.15.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/argparse": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "13.15.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/type-fest": {
+ "version": "0.20.2",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@gar/promisify": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@hapi/hoek": {
+ "version": "9.3.0",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@hapi/topo": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
+ "node_modules/@humanwhocodes/config-array": {
+ "version": "0.9.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanwhocodes/object-schema": "^1.2.1",
+ "debug": "^4.1.1",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ }
+ },
+ "node_modules/@humanwhocodes/object-schema": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@istanbuljs/load-nyc-config": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.3.1",
+ "find-up": "^4.1.0",
+ "get-package-type": "^0.1.0",
+ "js-yaml": "^3.13.1",
+ "resolve-from": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.0.0",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.4.13",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@lit/reactive-element": {
+ "version": "1.3.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@neuralegion/cypress-har-generator": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "chrome-remote-interface": "~0.30.0",
+ "tslib": "^2.2.0"
+ }
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@neuralegion/cypress-har-generator/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@ngrx/effects": {
+ "version": "13.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^13.0.0",
+ "@ngrx/store": "13.2.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@ngrx/router-store": {
+ "version": "13.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^13.0.0",
+ "@angular/core": "^13.0.0",
+ "@angular/router": "^13.0.0",
+ "@ngrx/store": "13.2.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@ngrx/schematics": {
+ "version": "13.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@ngrx/store": {
+ "version": "13.2.0",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "^13.0.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@ngrx/store-devtools": {
+ "version": "13.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@ngrx/store": "13.2.0",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@ngtools/webpack": {
+ "version": "13.3.7",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^13.0.0",
+ "typescript": ">=4.4.3 <4.7",
+ "webpack": "^5.30.0"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@npmcli/fs": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promisify": "^1.0.1",
+ "semver": "^7.3.5"
+ }
+ },
+ "node_modules/@npmcli/git": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/promise-spawn": "^1.3.2",
+ "lru-cache": "^6.0.0",
+ "mkdirp": "^1.0.4",
+ "npm-pick-manifest": "^6.1.1",
+ "promise-inflight": "^1.0.1",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^2.0.2"
+ }
+ },
+ "node_modules/@npmcli/installed-package-contents": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-bundled": "^1.1.1",
+ "npm-normalize-package-bin": "^1.0.1"
+ },
+ "bin": {
+ "installed-package-contents": "index.js"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@npmcli/move-file": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@npmcli/node-gyp": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@npmcli/promise-spawn": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "infer-owner": "^1.0.4"
+ }
+ },
+ "node_modules/@npmcli/run-script": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/node-gyp": "^1.0.2",
+ "@npmcli/promise-spawn": "^1.3.2",
+ "node-gyp": "^8.2.0",
+ "read-package-json-fast": "^2.0.1"
+ }
+ },
+ "node_modules/@nrwl/cli": {
+ "version": "14.1.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nx": "14.1.9"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/@nrwl/tao": {
+ "version": "14.1.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nx": "14.1.9"
+ },
+ "bin": {
+ "tao": "index.js"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/chalk": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@nrwl/cli/node_modules/fast-glob": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/glob": {
+ "version": "7.1.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/minimatch": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/nx": {
+ "version": "14.1.9",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nrwl/cli": "14.1.9",
+ "@nrwl/tao": "14.1.9",
+ "@parcel/watcher": "2.0.4",
+ "@swc-node/register": "^1.4.2",
+ "@swc/core": "^1.2.173",
+ "chalk": "4.1.0",
+ "chokidar": "^3.5.1",
+ "cli-cursor": "3.1.0",
+ "cli-spinners": "2.6.1",
+ "cliui": "^7.0.2",
+ "dotenv": "~10.0.0",
+ "enquirer": "~2.3.6",
+ "fast-glob": "3.2.7",
+ "figures": "3.2.0",
+ "flat": "^5.0.2",
+ "fs-extra": "^10.1.0",
+ "glob": "7.1.4",
+ "ignore": "^5.0.4",
+ "jsonc-parser": "3.0.0",
+ "minimatch": "3.0.4",
+ "npm-run-path": "^4.0.1",
+ "open": "^8.4.0",
+ "semver": "7.3.4",
+ "string-width": "^4.2.3",
+ "tar-stream": "~2.2.0",
+ "tmp": "~0.2.1",
+ "tsconfig-paths": "^3.9.0",
+ "tslib": "^2.3.0",
+ "v8-compile-cache": "2.3.0",
+ "yargs": "^17.4.0",
+ "yargs-parser": "21.0.1"
+ },
+ "bin": {
+ "nx": "bin/nx.js"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/semver": {
+ "version": "7.3.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@nrwl/cli/node_modules/yargs-parser": {
+ "version": "21.0.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@nrwl/devkit": {
+ "version": "13.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nrwl/tao": "13.1.3",
+ "ejs": "^3.1.5",
+ "ignore": "^5.0.4",
+ "rxjs": "^6.5.4",
+ "semver": "7.3.4",
+ "tslib": "^2.0.0"
+ }
+ },
+ "node_modules/@nrwl/devkit/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@nrwl/devkit/node_modules/rxjs/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@nrwl/devkit/node_modules/semver": {
+ "version": "7.3.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@nrwl/tao": {
+ "version": "13.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "4.1.0",
+ "enquirer": "~2.3.6",
+ "fs-extra": "^9.1.0",
+ "jsonc-parser": "3.0.0",
+ "nx": "13.1.3",
+ "rxjs": "^6.5.4",
+ "rxjs-for-await": "0.0.2",
+ "semver": "7.3.4",
+ "tmp": "~0.2.1",
+ "tslib": "^2.0.0",
+ "yargs-parser": "20.0.0"
+ },
+ "bin": {
+ "tao": "index.js"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/chalk": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@nrwl/tao/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/rxjs": {
+ "version": "6.6.7",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^1.9.0"
+ },
+ "engines": {
+ "npm": ">=2.0.0"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/rxjs/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@nrwl/tao/node_modules/semver": {
+ "version": "7.3.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@nrwl/tao/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.0.4",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "node-addon-api": "^3.2.1",
+ "node-gyp-build": "^4.3.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@rollup/plugin-json": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^3.0.8"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0 || ^2.0.0"
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve": {
+ "version": "13.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^3.1.0",
+ "@types/resolve": "1.17.1",
+ "deepmerge": "^4.2.2",
+ "is-builtin-module": "^3.1.0",
+ "is-module": "^1.0.0",
+ "resolve": "^1.19.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.42.0"
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "0.0.39",
+ "estree-walker": "^1.0.1",
+ "picomatch": "^2.2.2"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0"
+ }
+ },
+ "node_modules/@schematics/angular": {
+ "version": "13.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "13.3.7",
+ "@angular-devkit/schematics": "13.3.7",
+ "jsonc-parser": "3.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@sideway/address": {
+ "version": "4.1.4",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0"
+ }
+ },
+ "node_modules/@sideway/formula": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@sideway/pinpoint": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "0.14.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@swc-node/core": {
+ "version": "1.9.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@swc/core": "^1.2.172"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ }
+ },
+ "node_modules/@swc-node/register": {
+ "version": "1.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@swc-node/core": "^1.9.0",
+ "@swc-node/sourcemap-support": "^0.2.0",
+ "colorette": "^2.0.16",
+ "debug": "^4.3.4",
+ "pirates": "^4.0.5",
+ "tslib": "^2.4.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "typescript": ">= 4.3"
+ }
+ },
+ "node_modules/@swc-node/register/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@swc-node/register/node_modules/tslib": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/@swc-node/sourcemap-support": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map-support": "^0.5.21"
+ }
+ },
+ "node_modules/@swc/core": {
+ "version": "1.2.194",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "swcx": "run_swcx.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/swc"
+ },
+ "optionalDependencies": {
+ "@swc/core-android-arm-eabi": "1.2.194",
+ "@swc/core-android-arm64": "1.2.194",
+ "@swc/core-darwin-arm64": "1.2.194",
+ "@swc/core-darwin-x64": "1.2.194",
+ "@swc/core-freebsd-x64": "1.2.194",
+ "@swc/core-linux-arm-gnueabihf": "1.2.194",
+ "@swc/core-linux-arm64-gnu": "1.2.194",
+ "@swc/core-linux-arm64-musl": "1.2.194",
+ "@swc/core-linux-x64-gnu": "1.2.194",
+ "@swc/core-linux-x64-musl": "1.2.194",
+ "@swc/core-win32-arm64-msvc": "1.2.194",
+ "@swc/core-win32-ia32-msvc": "1.2.194",
+ "@swc/core-win32-x64-msvc": "1.2.194"
+ }
+ },
+ "node_modules/@swc/core-darwin-arm64": {
+ "version": "1.2.194",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@swimlane/ngx-graph": {
+ "version": "8.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "d3-dispatch": "^1.0.3",
+ "d3-ease": "^1.0.5",
+ "d3-force": "^1.1.0",
+ "d3-selection": "^1.2.0",
+ "d3-shape": "^1.2.0",
+ "d3-timer": "^1.0.7",
+ "d3-transition": "^1.1.1",
+ "dagre": "^0.8.4",
+ "transformation-matrix": "^1.15.3",
+ "tslib": "^2.0.0",
+ "webcola": "^3.3.8"
+ },
+ "peerDependencies": {
+ "@angular/animations": "10.x || 11.x || 12.x || 13.x",
+ "@angular/cdk": "10.x || 11.x || 12.x || 13.x",
+ "@angular/common": "10.x || 11.x || 12.x || 13.x",
+ "@angular/core": "10.x || 11.x || 12.x || 13.x",
+ "rxjs": "6.x"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/bonjour": {
+ "version": "3.5.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/component-emitter": {
+ "version": "1.2.11",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.35",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect-history-api-fallback": {
+ "version": "1.3.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/express-serve-static-core": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cookie": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.12",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/eslint": {
+ "version": "8.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint-scope": {
+ "version": "3.7.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint": "*",
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "0.0.39",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.13",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.18",
+ "@types/qs": "*",
+ "@types/serve-static": "*"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.17.28",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*"
+ }
+ },
+ "node_modules/@types/http-proxy": {
+ "version": "1.17.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/jasmine": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.11",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/lodash": {
+ "version": "4.14.182",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "17.0.35",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/parse-json": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/qs": {
+ "version": "6.9.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/resize-observer-browser": {
+ "version": "0.1.7",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/resolve": {
+ "version": "1.17.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/serve-index": {
+ "version": "1.9.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/express": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.13.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/sinonjs__fake-timers": {
+ "version": "8.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/sizzle": {
+ "version": "2.3.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/sockjs": {
+ "version": "0.3.33",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.2",
+ "license": "MIT"
+ },
+ "node_modules/@types/ws": {
+ "version": "8.5.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/yargs": {
+ "version": "17.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs-parser": "*"
+ }
+ },
+ "node_modules/@types/yargs-parser": {
+ "version": "21.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.26.0",
+ "@typescript-eslint/type-utils": "5.26.0",
+ "@typescript-eslint/utils": "5.26.0",
+ "debug": "^4.3.4",
+ "functional-red-black-tree": "^1.0.1",
+ "ignore": "^5.2.0",
+ "regexpp": "^3.2.0",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^5.0.0",
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": {
+ "version": "7.3.7",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "5.17.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.17.0",
+ "@typescript-eslint/visitor-keys": "5.17.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/types": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "5.17.0",
+ "@typescript-eslint/visitor-keys": "5.17.0",
+ "debug": "^4.3.2",
+ "globby": "^11.0.4",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.5",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/utils": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@typescript-eslint/scope-manager": "5.17.0",
+ "@typescript-eslint/types": "5.17.0",
+ "@typescript-eslint/typescript-estree": "5.17.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/experimental-utils/node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.17.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.17.0",
+ "eslint-visitor-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "5.26.0",
+ "@typescript-eslint/types": "5.26.0",
+ "@typescript-eslint/typescript-estree": "5.26.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/parser/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.26.0",
+ "@typescript-eslint/visitor-keys": "5.26.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "5.26.0",
+ "debug": "^4.3.4",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@typescript-eslint/types": "5.26.0",
+ "@typescript-eslint/visitor-keys": "5.26.0",
+ "debug": "^4.3.4",
+ "globby": "^11.1.0",
+ "is-glob": "^4.0.3",
+ "semver": "^7.3.7",
+ "tsutils": "^3.21.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
+ "version": "7.3.7",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "@typescript-eslint/scope-manager": "5.26.0",
+ "@typescript-eslint/types": "5.26.0",
+ "@typescript-eslint/typescript-estree": "5.26.0",
+ "eslint-scope": "^5.1.1",
+ "eslint-utils": "^3.0.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "5.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "5.26.0",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@webassemblyjs/ast": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/helper-numbers": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/floating-point-hex-parser": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-api-error": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-buffer": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-numbers": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/floating-point-hex-parser": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/helper-wasm-bytecode": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/helper-wasm-section": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/ieee754": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@xtuc/ieee754": "^1.2.0"
+ }
+ },
+ "node_modules/@webassemblyjs/leb128": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webassemblyjs/utf8": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@webassemblyjs/wasm-edit": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/helper-wasm-section": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-opt": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "@webassemblyjs/wast-printer": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-gen": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-opt": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-buffer": "1.11.1",
+ "@webassemblyjs/wasm-gen": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wasm-parser": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/helper-api-error": "1.11.1",
+ "@webassemblyjs/helper-wasm-bytecode": "1.11.1",
+ "@webassemblyjs/ieee754": "1.11.1",
+ "@webassemblyjs/leb128": "1.11.1",
+ "@webassemblyjs/utf8": "1.11.1"
+ }
+ },
+ "node_modules/@webassemblyjs/wast-printer": {
+ "version": "1.11.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@webassemblyjs/ast": "1.11.1",
+ "@xtuc/long": "4.2.2"
+ }
+ },
+ "node_modules/@webcomponents/custom-elements": {
+ "version": "1.5.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@xtuc/ieee754": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/@xtuc/long": {
+ "version": "4.2.2",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/abab": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/abbrev": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.7.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-import-assertions": {
+ "version": "1.8.0",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^8"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/adjust-sourcemap-loader": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "loader-utils": "^2.0.0",
+ "regex-parser": "^2.2.11"
+ },
+ "engines": {
+ "node": ">=8.9"
+ }
+ },
+ "node_modules/adjust-sourcemap-loader/node_modules/loader-utils": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/agentkeepalive": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.0",
+ "depd": "^1.1.2",
+ "humanize-ms": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
+ "node_modules/agentkeepalive/node_modules/depd": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/aggregate-error": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clean-stack": "^2.0.0",
+ "indent-string": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.9.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/angular-ng-autocomplete": {
+ "version": "2.0.8",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "*",
+ "@angular/core": "*"
+ }
+ },
+ "node_modules/angular-oauth2-oidc": {
+ "version": "13.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-sha256": "^1.3.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=12.0.0",
+ "@angular/core": ">=12.0.0"
+ }
+ },
+ "node_modules/ansi-align": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.1.0"
+ }
+ },
+ "node_modules/ansi-colors": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-html-community": {
+ "version": "0.0.8",
+ "dev": true,
+ "engines": [
+ "node >= 0.8.0"
+ ],
+ "license": "Apache-2.0",
+ "bin": {
+ "ansi-html": "bin/ansi-html"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/aproba": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/arch": {
+ "version": "2.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/are-we-there-yet": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/aria-query": {
+ "version": "4.2.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@babel/runtime": "^7.10.2",
+ "@babel/runtime-corejs3": "^7.10.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5",
+ "get-intrinsic": "^1.1.1",
+ "is-string": "^1.0.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-union": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.2",
+ "es-shim-unscopables": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/asn1": {
+ "version": "0.2.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": "~2.1.0"
+ }
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/atob": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "(MIT OR Apache-2.0)",
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "engines": {
+ "node": ">= 4.5.0"
+ }
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.7",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.20.3",
+ "caniuse-lite": "^1.0.30001335",
+ "fraction.js": "^4.2.0",
+ "normalize-range": "^0.1.2",
+ "picocolors": "^1.0.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/aws-sign2": {
+ "version": "0.7.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/aws4": {
+ "version": "1.11.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/axios": {
+ "version": "0.21.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.14.0"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/babel-loader": {
+ "version": "8.2.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-cache-dir": "^3.3.1",
+ "loader-utils": "^2.0.0",
+ "make-dir": "^3.1.0",
+ "schema-utils": "^2.6.5"
+ },
+ "engines": {
+ "node": ">= 8.9"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0",
+ "webpack": ">=2"
+ }
+ },
+ "node_modules/babel-loader/node_modules/loader-utils": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/babel-plugin-dynamic-import-node": {
+ "version": "2.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object.assign": "^4.1.0"
+ }
+ },
+ "node_modules/babel-plugin-istanbul": {
+ "version": "6.1.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@istanbuljs/load-nyc-config": "^1.0.0",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-instrument": "^5.0.4",
+ "test-exclude": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.13.11",
+ "@babel/helper-define-polyfill-provider": "^0.3.1",
+ "semver": "^6.1.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.5.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.3.1",
+ "core-js-compat": "^3.21.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/batch": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bcrypt-pbkdf": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tweetnacl": "^0.14.3"
+ }
+ },
+ "node_modules/bent": {
+ "version": "7.3.12",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bytesish": "^0.4.1",
+ "caseless": "~0.12.0",
+ "is-stream": "^2.0.0"
+ }
+ },
+ "node_modules/big.js": {
+ "version": "5.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/blob-util": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.4",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "on-finished": "2.4.1",
+ "qs": "6.10.3",
+ "raw-body": "2.5.1",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/body-parser/node_modules/qs": {
+ "version": "6.10.3",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/bonjour": {
+ "version": "3.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-flatten": "^2.1.0",
+ "deep-equal": "^1.0.1",
+ "dns-equal": "^1.0.0",
+ "dns-txt": "^2.0.2",
+ "multicast-dns": "^6.0.1",
+ "multicast-dns-service-types": "^1.1.0"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/boxen": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-align": "^3.0.0",
+ "camelcase": "^6.2.0",
+ "chalk": "^4.1.0",
+ "cli-boxes": "^2.2.1",
+ "string-width": "^4.2.2",
+ "type-fest": "^0.20.2",
+ "widest-line": "^3.1.0",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/camelcase": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/boxen/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/boxen/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/boxen/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/boxen/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/boxen/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/boxen/node_modules/type-fest": {
+ "version": "0.20.2",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.20.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001332",
+ "electron-to-chromium": "^1.4.118",
+ "escalade": "^3.1.1",
+ "node-releases": "^2.0.3",
+ "picocolors": "^1.0.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/buffer-indexof": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builtin-modules": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/builtins": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/bytesish": {
+ "version": "0.4.4",
+ "dev": true,
+ "license": "(Apache-2.0 AND MIT)"
+ },
+ "node_modules/cacache": {
+ "version": "15.3.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^1.0.0",
+ "@npmcli/move-file": "^1.0.1",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "glob": "^7.1.4",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.1",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.2",
+ "mkdirp": "^1.0.3",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.0.2",
+ "unique-filename": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-response": "^1.0.2",
+ "get-stream": "^5.1.0",
+ "http-cache-semantics": "^4.0.0",
+ "keyv": "^3.0.0",
+ "lowercase-keys": "^2.0.0",
+ "normalize-url": "^4.1.0",
+ "responselike": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cacheable-request/node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cachedir": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "get-intrinsic": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "5.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001343",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/chalk": {
+ "version": "2.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^3.2.1",
+ "escape-string-regexp": "^1.0.5",
+ "supports-color": "^5.3.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "0.7.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/check-more-types": {
+ "version": "2.24.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "3.5.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/chrome-remote-interface": {
+ "version": "0.30.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "2.11.x",
+ "ws": "^7.2.0"
+ },
+ "bin": {
+ "chrome-remote-interface": "bin/client.js"
+ }
+ },
+ "node_modules/chrome-trace-event": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "3.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/circular-dependency-plugin": {
+ "version": "5.2.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "peerDependencies": {
+ "webpack": ">=4.0.1"
+ }
+ },
+ "node_modules/clean-stack": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/cli-boxes": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.6.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-table3": {
+ "version": "0.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "7.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/clone-response": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "1.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "1.1.3"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.16",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colors": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.11.0",
+ "license": "MIT"
+ },
+ "node_modules/comment-parser": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/commondir": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/component-emitter": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.7.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.5",
+ "bytes": "3.0.0",
+ "compressible": "~2.0.16",
+ "debug": "2.6.9",
+ "on-headers": "~1.0.2",
+ "safe-buffer": "5.1.2",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/bytes": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/configstore": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dot-prop": "^5.2.0",
+ "graceful-fs": "^4.1.2",
+ "make-dir": "^3.0.0",
+ "unique-string": "^2.0.0",
+ "write-file-atomic": "^3.0.0",
+ "xdg-basedir": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/connect": {
+ "version": "3.7.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "finalhandler": "1.1.2",
+ "parseurl": "~1.3.3",
+ "utils-merge": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/connect-history-api-fallback": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/connect/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/connect/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/console-control-strings": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-disposition/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/content-type": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.1"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.4.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/copy-anything": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^3.14.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/copy-webpack-plugin": {
+ "version": "10.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "^3.2.7",
+ "glob-parent": "^6.0.1",
+ "globby": "^12.0.2",
+ "normalize-path": "^3.0.0",
+ "schema-utils": "^4.0.0",
+ "serialize-javascript": "^6.0.0"
+ },
+ "engines": {
+ "node": ">= 12.20.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/array-union": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/globby": {
+ "version": "12.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^3.0.1",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.7",
+ "ignore": "^5.1.9",
+ "merge2": "^1.4.1",
+ "slash": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/copy-webpack-plugin/node_modules/slash": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/core-js": {
+ "version": "3.20.3",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.22.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.20.3",
+ "semver": "7.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-js-compat/node_modules/semver": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/core-js-pure": {
+ "version": "3.22.7",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cosmiconfig": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/parse-json": "^4.0.0",
+ "import-fresh": "^3.2.1",
+ "parse-json": "^5.0.0",
+ "path-type": "^4.0.0",
+ "yaml": "^1.10.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/critters": {
+ "version": "0.0.16",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "css-select": "^4.2.0",
+ "parse5": "^6.0.1",
+ "parse5-htmlparser2-tree-adapter": "^6.0.1",
+ "postcss": "^8.3.7",
+ "pretty-bytes": "^5.3.0"
+ }
+ },
+ "node_modules/critters/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/critters/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/critters/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/critters/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/critters/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/critters/node_modules/parse5": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/critters/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-random-string": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/css": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.4",
+ "source-map": "^0.6.1",
+ "source-map-resolve": "^0.6.0"
+ }
+ },
+ "node_modules/css-blank-pseudo": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "bin": {
+ "css-blank-pseudo": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-has-pseudo": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "bin": {
+ "css-has-pseudo": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-loader": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "icss-utils": "^5.1.0",
+ "postcss": "^8.2.15",
+ "postcss-modules-extract-imports": "^3.0.0",
+ "postcss-modules-local-by-default": "^4.0.0",
+ "postcss-modules-scope": "^3.0.0",
+ "postcss-modules-values": "^4.0.0",
+ "postcss-value-parser": "^4.1.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/css-prefers-color-scheme": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "bin": {
+ "css-prefers-color-scheme": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.0.1",
+ "domhandler": "^4.3.1",
+ "domutils": "^2.8.0",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/cssdb": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/cuint": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/custom-event": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress": {
+ "version": "9.7.0",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cypress/request": "^2.88.10",
+ "@cypress/xvfb": "^1.2.4",
+ "@types/node": "^14.14.31",
+ "@types/sinonjs__fake-timers": "8.1.1",
+ "@types/sizzle": "^2.3.2",
+ "arch": "^2.2.0",
+ "blob-util": "^2.0.2",
+ "bluebird": "^3.7.2",
+ "buffer": "^5.6.0",
+ "cachedir": "^2.3.0",
+ "chalk": "^4.1.0",
+ "check-more-types": "^2.24.0",
+ "cli-cursor": "^3.1.0",
+ "cli-table3": "~0.6.1",
+ "commander": "^5.1.0",
+ "common-tags": "^1.8.0",
+ "dayjs": "^1.10.4",
+ "debug": "^4.3.2",
+ "enquirer": "^2.3.6",
+ "eventemitter2": "^6.4.3",
+ "execa": "4.1.0",
+ "executable": "^4.1.1",
+ "extract-zip": "2.0.1",
+ "figures": "^3.2.0",
+ "fs-extra": "^9.1.0",
+ "getos": "^3.2.1",
+ "is-ci": "^3.0.0",
+ "is-installed-globally": "~0.4.0",
+ "lazy-ass": "^1.6.0",
+ "listr2": "^3.8.3",
+ "lodash": "^4.17.21",
+ "log-symbols": "^4.0.0",
+ "minimist": "^1.2.6",
+ "ospath": "^1.2.2",
+ "pretty-bytes": "^5.6.0",
+ "proxy-from-env": "1.0.0",
+ "request-progress": "^3.0.0",
+ "semver": "^7.3.2",
+ "supports-color": "^8.1.1",
+ "tmp": "~0.2.1",
+ "untildify": "^4.0.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "cypress": "bin/cypress"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/cypress-grep": {
+ "version": "2.13.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "debug": "4.3.1",
+ "find-test-names": "^1.3.2",
+ "globby": "^11.0.4"
+ }
+ },
+ "node_modules/cypress-grep/node_modules/debug": {
+ "version": "4.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/cypress-localstorage-commands": {
+ "version": "1.7.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "cypress": ">=2.1.0"
+ }
+ },
+ "node_modules/cypress-terminal-report": {
+ "version": "3.5.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^3.0.0",
+ "fs-extra": "^9.0.1",
+ "methods": "^1.1.2",
+ "semver": "^7.3.5",
+ "tv4": "^1.3.0"
+ },
+ "peerDependencies": {
+ "cypress": ">=4.10.0"
+ }
+ },
+ "node_modules/cypress-terminal-report/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cypress-terminal-report/node_modules/chalk": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress-terminal-report/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cypress-terminal-report/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress-terminal-report/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress-terminal-report/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/@types/node": {
+ "version": "14.18.18",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cypress/node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/cypress/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cypress/node_modules/commander": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cypress/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cypress/node_modules/supports-color": {
+ "version": "8.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/d3": {
+ "version": "5.16.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1",
+ "d3-axis": "1",
+ "d3-brush": "1",
+ "d3-chord": "1",
+ "d3-collection": "1",
+ "d3-color": "1",
+ "d3-contour": "1",
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-dsv": "1",
+ "d3-ease": "1",
+ "d3-fetch": "1",
+ "d3-force": "1",
+ "d3-format": "1",
+ "d3-geo": "1",
+ "d3-hierarchy": "1",
+ "d3-interpolate": "1",
+ "d3-path": "1",
+ "d3-polygon": "1",
+ "d3-quadtree": "1",
+ "d3-random": "1",
+ "d3-scale": "2",
+ "d3-scale-chromatic": "1",
+ "d3-selection": "1",
+ "d3-shape": "1",
+ "d3-time": "1",
+ "d3-time-format": "2",
+ "d3-timer": "1",
+ "d3-transition": "1",
+ "d3-voronoi": "1",
+ "d3-zoom": "1"
+ }
+ },
+ "node_modules/d3-array": {
+ "version": "1.2.4",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-axis": {
+ "version": "1.0.12",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-brush": {
+ "version": "1.1.6",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
+ },
+ "node_modules/d3-chord": {
+ "version": "1.0.6",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1",
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-collection": {
+ "version": "1.0.7",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-color": {
+ "version": "1.4.1",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-contour": {
+ "version": "1.3.2",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "^1.1.1"
+ }
+ },
+ "node_modules/d3-dispatch": {
+ "version": "1.0.6",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-drag": {
+ "version": "1.2.5",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-dispatch": "1",
+ "d3-selection": "1"
+ }
+ },
+ "node_modules/d3-dsv": {
+ "version": "1.2.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "commander": "2",
+ "iconv-lite": "0.4",
+ "rw": "1"
+ },
+ "bin": {
+ "csv2json": "bin/dsv2json",
+ "csv2tsv": "bin/dsv2dsv",
+ "dsv2dsv": "bin/dsv2dsv",
+ "dsv2json": "bin/dsv2json",
+ "json2csv": "bin/json2dsv",
+ "json2dsv": "bin/json2dsv",
+ "json2tsv": "bin/json2dsv",
+ "tsv2csv": "bin/dsv2dsv",
+ "tsv2json": "bin/dsv2json"
+ }
+ },
+ "node_modules/d3-ease": {
+ "version": "1.0.7",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-fetch": {
+ "version": "1.2.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-dsv": "1"
+ }
+ },
+ "node_modules/d3-force": {
+ "version": "1.2.1",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-collection": "1",
+ "d3-dispatch": "1",
+ "d3-quadtree": "1",
+ "d3-timer": "1"
+ }
+ },
+ "node_modules/d3-format": {
+ "version": "1.4.5",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-geo": {
+ "version": "1.12.1",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "1"
+ }
+ },
+ "node_modules/d3-hierarchy": {
+ "version": "1.1.9",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-interpolate": {
+ "version": "1.4.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-color": "1"
+ }
+ },
+ "node_modules/d3-path": {
+ "version": "1.0.9",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-polygon": {
+ "version": "1.0.6",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-quadtree": {
+ "version": "1.0.7",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-random": {
+ "version": "1.1.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-scale": {
+ "version": "2.2.2",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-array": "^1.2.0",
+ "d3-collection": "1",
+ "d3-format": "1",
+ "d3-interpolate": "1",
+ "d3-time": "1",
+ "d3-time-format": "2"
+ }
+ },
+ "node_modules/d3-scale-chromatic": {
+ "version": "1.5.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-color": "1",
+ "d3-interpolate": "1"
+ }
+ },
+ "node_modules/d3-selection": {
+ "version": "1.4.2",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-shape": {
+ "version": "1.3.7",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-path": "1"
+ }
+ },
+ "node_modules/d3-time": {
+ "version": "1.1.0",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-time-format": {
+ "version": "2.3.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-time": "1"
+ }
+ },
+ "node_modules/d3-timer": {
+ "version": "1.0.10",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-transition": {
+ "version": "1.3.2",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-color": "1",
+ "d3-dispatch": "1",
+ "d3-ease": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "^1.1.0",
+ "d3-timer": "1"
+ }
+ },
+ "node_modules/d3-voronoi": {
+ "version": "1.1.4",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/d3-zoom": {
+ "version": "1.8.3",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "d3-dispatch": "1",
+ "d3-drag": "1",
+ "d3-interpolate": "1",
+ "d3-selection": "1",
+ "d3-transition": "1"
+ }
+ },
+ "node_modules/dagre": {
+ "version": "0.8.5",
+ "license": "MIT",
+ "dependencies": {
+ "graphlib": "^2.1.8",
+ "lodash": "^4.17.15"
+ }
+ },
+ "node_modules/dashdash": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/date-format": {
+ "version": "4.0.10",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/dayjs": {
+ "version": "1.11.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decode-uri-component": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/deep-equal": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arguments": "^1.0.4",
+ "is-date-object": "^1.0.1",
+ "is-regex": "^1.0.4",
+ "object-is": "^1.0.1",
+ "object-keys": "^1.1.1",
+ "regexp.prototype.flags": "^1.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/deep-extend": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deepmerge": {
+ "version": "4.2.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/default-gateway": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "execa": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/default-gateway/node_modules/execa": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/default-gateway/node_modules/get-stream": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-gateway/node_modules/human-signals": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone": "^1.0.2"
+ }
+ },
+ "node_modules/defer-to-connect": {
+ "version": "1.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/del": {
+ "version": "6.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "globby": "^11.0.1",
+ "graceful-fs": "^4.2.4",
+ "is-glob": "^4.0.1",
+ "is-path-cwd": "^2.2.0",
+ "is-path-inside": "^3.0.2",
+ "p-map": "^4.0.0",
+ "rimraf": "^3.0.2",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/delegates": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dependency-graph": {
+ "version": "0.11.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/di": {
+ "version": "0.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dir-glob": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-type": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dns-equal": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dns-packet": {
+ "version": "1.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip": "^1.1.0",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/dns-txt": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-indexof": "^1.0.0"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/dom-serialize": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "custom-event": "~1.0.0",
+ "ent": "~2.2.0",
+ "extend": "^3.0.0",
+ "void-elements": "^2.0.0"
+ }
+ },
+ "node_modules/dom-serializer": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.0.1",
+ "domhandler": "^4.2.0",
+ "entities": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "4.3.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.2.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/dompurify": {
+ "version": "2.3.8",
+ "license": "(MPL-2.0 OR Apache-2.0)"
+ },
+ "node_modules/domutils": {
+ "version": "2.8.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^1.0.1",
+ "domelementtype": "^2.2.0",
+ "domhandler": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "10.0.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/duplexer": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/duplexer3": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ecc-jsbn": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.1.0"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ejs": {
+ "version": "3.1.8",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.4.139",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emojis-list": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/encodeurl": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/encoding/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cookie": "^0.4.1",
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.4.1",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.0.3",
+ "ws": "~8.2.3"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io/node_modules/ws": {
+ "version": "8.2.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.9.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/enquirer": {
+ "version": "2.3.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-colors": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/ent": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/err-code": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/errno": {
+ "version": "0.1.8",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "prr": "~1.0.1"
+ },
+ "bin": {
+ "errno": "cli.js"
+ }
+ },
+ "node_modules/error-ex": {
+ "version": "1.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-arrayish": "^0.2.1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.20.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "function.prototype.name": "^1.1.5",
+ "get-intrinsic": "^1.1.1",
+ "get-symbol-description": "^1.0.0",
+ "has": "^1.0.3",
+ "has-property-descriptors": "^1.0.0",
+ "has-symbols": "^1.0.3",
+ "internal-slot": "^1.0.3",
+ "is-callable": "^1.2.4",
+ "is-negative-zero": "^2.0.2",
+ "is-regex": "^1.1.4",
+ "is-shared-array-buffer": "^1.0.2",
+ "is-string": "^1.0.7",
+ "is-weakref": "^1.0.2",
+ "object-inspect": "^1.12.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.2",
+ "regexp.prototype.flags": "^1.4.3",
+ "string.prototype.trimend": "^1.0.5",
+ "string.prototype.trimstart": "^1.0.5",
+ "unbox-primitive": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "0.9.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has": "^1.0.3"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.14.22",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "esbuild-android-arm64": "0.14.22",
+ "esbuild-darwin-64": "0.14.22",
+ "esbuild-darwin-arm64": "0.14.22",
+ "esbuild-freebsd-64": "0.14.22",
+ "esbuild-freebsd-arm64": "0.14.22",
+ "esbuild-linux-32": "0.14.22",
+ "esbuild-linux-64": "0.14.22",
+ "esbuild-linux-arm": "0.14.22",
+ "esbuild-linux-arm64": "0.14.22",
+ "esbuild-linux-mips64le": "0.14.22",
+ "esbuild-linux-ppc64le": "0.14.22",
+ "esbuild-linux-riscv64": "0.14.22",
+ "esbuild-linux-s390x": "0.14.22",
+ "esbuild-netbsd-64": "0.14.22",
+ "esbuild-openbsd-64": "0.14.22",
+ "esbuild-sunos-64": "0.14.22",
+ "esbuild-windows-32": "0.14.22",
+ "esbuild-windows-64": "0.14.22",
+ "esbuild-windows-arm64": "0.14.22"
+ }
+ },
+ "node_modules/esbuild-darwin-arm64": {
+ "version": "0.14.22",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/esbuild-wasm": {
+ "version": "0.14.22",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-goat": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "8.16.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint/eslintrc": "^1.3.0",
+ "@humanwhocodes/config-array": "^0.9.2",
+ "ajv": "^6.10.0",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.1.1",
+ "eslint-utils": "^3.0.0",
+ "eslint-visitor-keys": "^3.3.0",
+ "espree": "^9.3.2",
+ "esquery": "^1.4.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "functional-red-black-tree": "^1.0.1",
+ "glob-parent": "^6.0.1",
+ "globals": "^13.15.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.0.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.1",
+ "regexpp": "^3.2.0",
+ "strip-ansi": "^6.0.1",
+ "strip-json-comments": "^3.1.0",
+ "text-table": "^0.2.0",
+ "v8-compile-cache": "^2.0.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-etc": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "^5.0.0",
+ "tsutils": "^3.17.1",
+ "tsutils-etc": "^1.4.1"
+ },
+ "peerDependencies": {
+ "eslint": "^8.0.0",
+ "typescript": "^4.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "resolve": "^1.20.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript": {
+ "version": "2.7.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "debug": "^4.3.4",
+ "glob": "^7.2.0",
+ "is-glob": "^4.0.3",
+ "resolve": "^1.22.0",
+ "tsconfig-paths": "^3.14.1"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "*",
+ "eslint-plugin-import": "*"
+ }
+ },
+ "node_modules/eslint-import-resolver-typescript/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.7.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "find-up": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/find-up": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/locate-path": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/p-limit": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/p-locate": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/p-try": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/path-exists": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/eslint-plugin-cypress": {
+ "version": "2.12.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "globals": "^11.12.0"
+ },
+ "peerDependencies": {
+ "eslint": ">= 3.2.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.26.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.4",
+ "array.prototype.flat": "^1.2.5",
+ "debug": "^2.6.9",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.6",
+ "eslint-module-utils": "^2.7.3",
+ "has": "^1.0.3",
+ "is-core-module": "^2.8.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.values": "^1.1.5",
+ "resolve": "^1.22.0",
+ "tsconfig-paths": "^3.14.1"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/doctrine": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint-plugin-jasmine": {
+ "version": "4.1.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc": {
+ "version": "39.3.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@es-joy/jsdoccomment": "~0.31.0",
+ "comment-parser": "1.3.1",
+ "debug": "^4.3.4",
+ "escape-string-regexp": "^4.0.0",
+ "esquery": "^1.4.0",
+ "semver": "^7.3.7",
+ "spdx-expression-parse": "^3.0.1"
+ },
+ "engines": {
+ "node": "^14 || ^16 || ^17 || ^18"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc/node_modules/semver": {
+ "version": "7.3.7",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-plugin-ngrx": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/schematics": "^13.0.3",
+ "@typescript-eslint/experimental-utils": "^5.4.0",
+ "eslint-etc": "^5.1.0",
+ "semver": "^7.3.5",
+ "strip-json-comments": "3.1.1"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.0.0",
+ "typescript": ">=4.4.0"
+ }
+ },
+ "node_modules/eslint-plugin-prefer-arrow": {
+ "version": "1.2.3",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=2.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.28.0",
+ "prettier": ">=2.0.0"
+ },
+ "peerDependenciesMeta": {
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-plugin-rxjs": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/experimental-utils": "^5.0.0",
+ "common-tags": "^1.8.0",
+ "decamelize": "^5.0.0",
+ "eslint-etc": "^5.1.0",
+ "requireindex": "~1.2.0",
+ "rxjs-report-usage": "^1.0.4",
+ "tslib": "^2.0.0",
+ "tsutils": "^3.0.0",
+ "tsutils-etc": "^1.4.1"
+ },
+ "peerDependencies": {
+ "eslint": "^8.0.0",
+ "typescript": "^4.0.0"
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^4.1.1"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/eslint-utils": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^2.0.0"
+ },
+ "engines": {
+ "node": "^10.0.0 || ^12.0.0 || >= 14.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ },
+ "peerDependencies": {
+ "eslint": ">=5"
+ }
+ },
+ "node_modules/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/eslint/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/argparse": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/eslint/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/eslint/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-scope": {
+ "version": "7.1.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/eslint/node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/globals": {
+ "version": "13.15.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/js-yaml": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/eslint/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/eslint/node_modules/type-fest": {
+ "version": "0.20.2",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/espree": {
+ "version": "9.3.2",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.7.1",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esquery/node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esrecurse/node_modules/estraverse": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-stream": {
+ "version": "3.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "~0.1.1",
+ "from": "~0",
+ "map-stream": "~0.1.0",
+ "pause-stream": "0.0.11",
+ "split": "0.3",
+ "stream-combiner": "~0.0.4",
+ "through": "~2.3.1"
+ }
+ },
+ "node_modules/eventemitter-asyncresource": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eventemitter2": {
+ "version": "6.4.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/execa": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "get-stream": "^5.0.0",
+ "human-signals": "^1.1.1",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.0",
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/executable": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.18.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "1.20.0",
+ "content-disposition": "0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "0.5.0",
+ "cookie-signature": "1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "1.2.0",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "merge-descriptors": "1.0.1",
+ "methods": "~1.1.2",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "0.1.7",
+ "proxy-addr": "~2.0.7",
+ "qs": "6.10.3",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "0.18.0",
+ "serve-static": "1.15.0",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/express/node_modules/array-flatten": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/cookie": {
+ "version": "0.5.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/finalhandler": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "2.0.1",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/qs": {
+ "version": "6.10.3",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/express/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/express/node_modules/statuses": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/external-editor": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^0.7.0",
+ "iconv-lite": "^0.4.24",
+ "tmp": "^0.0.33"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/external-editor/node_modules/tmp": {
+ "version": "0.0.33",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "os-tmpdir": "~1.0.2"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.3.0",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-diff": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/fast-equals": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.2.11",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-sha256": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "Unlicense"
+ },
+ "node_modules/fastq": {
+ "version": "1.13.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/figures": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/filelist": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/filelist/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/filelist/node_modules/minimatch": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.3.0",
+ "parseurl": "~1.3.3",
+ "statuses": "~1.5.0",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/finalhandler/node_modules/on-finished": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/find-cache-dir": {
+ "version": "3.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commondir": "^1.0.1",
+ "make-dir": "^3.0.2",
+ "pkg-dir": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/avajs/find-cache-dir?sponsor=1"
+ }
+ },
+ "node_modules/find-test-names": {
+ "version": "1.17.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.16.5",
+ "acorn-walk": "^8.2.0",
+ "debug": "^4.3.3",
+ "globby": "^11.0.4",
+ "simple-bin-help": "^1.7.6"
+ },
+ "bin": {
+ "find-test-names": "bin/find-test-names.js",
+ "print-tests": "bin/print-tests.js",
+ "update-test-count": "bin/update-test-count.js"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^5.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.1.0",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.2.5",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/forever-agent": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "2.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.6",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 0.12"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "patreon",
+ "url": "https://www.patreon.com/infusion"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/from": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fs-extra": {
+ "version": "9.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-monkey": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "Unlicense"
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.0",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functional-red-black-tree": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gauge": {
+ "version": "4.0.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "aproba": "^1.0.3 || ^2.0.0",
+ "color-support": "^1.1.3",
+ "console-control-strings": "^1.1.0",
+ "has-unicode": "^2.0.1",
+ "signal-exit": "^3.0.7",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wide-align": "^1.1.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-package-type": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "get-intrinsic": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/getos": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "^3.2.0"
+ }
+ },
+ "node_modules/getpass": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/global-dirs": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ini": "2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globals": {
+ "version": "11.12.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/globby": {
+ "version": "11.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-union": "^2.1.0",
+ "dir-glob": "^3.0.1",
+ "fast-glob": "^3.2.9",
+ "ignore": "^5.2.0",
+ "merge2": "^1.4.1",
+ "slash": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/got": {
+ "version": "9.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^0.14.0",
+ "@szmarczak/http-timer": "^1.1.2",
+ "cacheable-request": "^6.0.0",
+ "decompress-response": "^3.3.0",
+ "duplexer3": "^0.1.4",
+ "get-stream": "^4.1.0",
+ "lowercase-keys": "^1.0.1",
+ "mimic-response": "^1.0.1",
+ "p-cancelable": "^1.0.0",
+ "to-readable-stream": "^1.0.0",
+ "url-parse-lax": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/got/node_modules/get-stream": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.10",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/graphlib": {
+ "version": "2.1.8",
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.15"
+ }
+ },
+ "node_modules/handle-thing": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/has-bigints": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-unicode": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-yarn": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hdr-histogram-js": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "BSD",
+ "dependencies": {
+ "@assemblyscript/loader": "^0.10.1",
+ "base64-js": "^1.2.0",
+ "pako": "^1.0.3"
+ }
+ },
+ "node_modules/hdr-histogram-percentiles-obj": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/hosted-git-info": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/hpack.js": {
+ "version": "2.1.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "obuf": "^1.0.0",
+ "readable-stream": "^2.0.1",
+ "wbuf": "^1.1.0"
+ }
+ },
+ "node_modules/hpack.js/node_modules/readable-stream": {
+ "version": "2.3.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/hpack.js/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/html-entities": {
+ "version": "2.3.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-deceiver": {
+ "version": "1.2.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "2.0.0",
+ "inherits": "2.0.4",
+ "setprototypeof": "1.2.0",
+ "statuses": "2.0.1",
+ "toidentifier": "1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-errors/node_modules/statuses": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-parser-js": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/http-proxy": {
+ "version": "1.18.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.0",
+ "follow-redirects": "^1.0.0",
+ "requires-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "1",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http-proxy-middleware": {
+ "version": "2.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-proxy": "^1.17.8",
+ "http-proxy": "^1.18.1",
+ "is-glob": "^4.0.1",
+ "is-plain-obj": "^3.0.0",
+ "micromatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "@types/express": "^4.17.13"
+ },
+ "peerDependenciesMeta": {
+ "@types/express": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/http-signature": {
+ "version": "1.3.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "jsprim": "^2.0.2",
+ "sshpk": "^1.14.1"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8.12.0"
+ }
+ },
+ "node_modules/humanize-ms": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.0.0"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/icss-utils": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/ignore-walk": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/image-size": {
+ "version": "0.5.5",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "image-size": "bin/image-size.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-fresh/node_modules/resolve-from": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/import-lazy": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/infer-owner": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/injection-js": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ }
+ },
+ "node_modules/inquirer": {
+ "version": "8.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^4.2.1",
+ "chalk": "^4.1.1",
+ "cli-cursor": "^3.1.0",
+ "cli-width": "^3.0.0",
+ "external-editor": "^3.0.3",
+ "figures": "^3.0.0",
+ "lodash": "^4.17.21",
+ "mute-stream": "0.0.8",
+ "ora": "^5.4.1",
+ "run-async": "^2.4.0",
+ "rxjs": "^7.2.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "through": "^2.3.6"
+ },
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/inquirer/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/inquirer/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/inquirer/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/inquirer/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/inquirer/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/internal-slot": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.1.0",
+ "has": "^1.0.3",
+ "side-channel": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ip": {
+ "version": "1.1.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/is-arguments": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-bigint": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-builtin-module": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-modules": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-callable": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-ci": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^3.2.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.9.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-date-object": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-installed-globally": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "global-dirs": "^3.0.0",
+ "is-path-inside": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-lambda": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-npm": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-path-cwd": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/is-path-inside": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-string": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-tostringtag": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-symbol": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typedarray": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-weakref": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-what": {
+ "version": "3.14.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-wsl": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-yarn-global": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/isbinaryfile": {
+ "version": "4.0.10",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/isstream": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.12.3",
+ "@babel/parser": "^7.14.7",
+ "@istanbuljs/schema": "^0.1.2",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^3.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-report/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "istanbul-lib-coverage": "^3.0.0",
+ "source-map": "^0.6.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.1.4",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jake": {
+ "version": "10.8.5",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "^3.2.3",
+ "chalk": "^4.0.2",
+ "filelist": "^1.0.1",
+ "minimatch": "^3.0.4"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/jake/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/jake/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/jake/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/jake/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jake/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jake/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jasmine-core": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jasmine-spec-reporter": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "colors": "1.4.0"
+ }
+ },
+ "node_modules/jest-worker": {
+ "version": "27.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "merge-stream": "^2.0.0",
+ "supports-color": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ }
+ },
+ "node_modules/jest-worker/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jest-worker/node_modules/supports-color": {
+ "version": "8.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/joi": {
+ "version": "17.6.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@hapi/hoek": "^9.0.0",
+ "@hapi/topo": "^5.0.0",
+ "@sideway/address": "^4.1.3",
+ "@sideway/formula": "^3.0.0",
+ "@sideway/pinpoint": "^2.0.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "3.14.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsbn": {
+ "version": "0.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsdoc-type-pratt-parser": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "2.5.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-better-errors": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "(AFL-2.1 OR BSD-3-Clause)"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/json5": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsonfile": {
+ "version": "6.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/jsprim": {
+ "version": "2.0.2",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.4.0",
+ "verror": "1.10.0"
+ }
+ },
+ "node_modules/karma": {
+ "version": "6.3.20",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@colors/colors": "1.5.0",
+ "body-parser": "^1.19.0",
+ "braces": "^3.0.2",
+ "chokidar": "^3.5.1",
+ "connect": "^3.7.0",
+ "di": "^0.0.1",
+ "dom-serialize": "^2.2.1",
+ "glob": "^7.1.7",
+ "graceful-fs": "^4.2.6",
+ "http-proxy": "^1.18.1",
+ "isbinaryfile": "^4.0.8",
+ "lodash": "^4.17.21",
+ "log4js": "^6.4.1",
+ "mime": "^2.5.2",
+ "minimatch": "^3.0.4",
+ "mkdirp": "^0.5.5",
+ "qjobs": "^1.2.0",
+ "range-parser": "^1.2.1",
+ "rimraf": "^3.0.2",
+ "socket.io": "^4.4.1",
+ "source-map": "^0.6.1",
+ "tmp": "^0.2.1",
+ "ua-parser-js": "^0.7.30",
+ "yargs": "^16.1.1"
+ },
+ "bin": {
+ "karma": "bin/karma"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/karma-chrome-launcher": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which": "^1.2.1"
+ }
+ },
+ "node_modules/karma-chrome-launcher/node_modules/which": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/karma-coverage": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.2.0",
+ "istanbul-lib-instrument": "^5.1.0",
+ "istanbul-lib-report": "^3.0.0",
+ "istanbul-lib-source-maps": "^4.0.1",
+ "istanbul-reports": "^3.0.5",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/karma-jasmine": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jasmine-core": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "peerDependencies": {
+ "karma": "^6.0.0"
+ }
+ },
+ "node_modules/karma-jasmine-html-reporter": {
+ "version": "1.7.0",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "jasmine-core": ">=3.8",
+ "karma": ">=0.9",
+ "karma-jasmine": ">=1.1"
+ }
+ },
+ "node_modules/karma-junit-reporter": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-is-absolute": "^1.0.0",
+ "xmlbuilder": "12.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ },
+ "peerDependencies": {
+ "karma": ">=0.9"
+ }
+ },
+ "node_modules/karma-source-map-support": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map-support": "^0.5.5"
+ }
+ },
+ "node_modules/karma/node_modules/mkdirp": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/karma/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/karma/node_modules/yargs": {
+ "version": "16.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^20.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/karma/node_modules/yargs-parser": {
+ "version": "20.2.9",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.0"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kleur": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/klona": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/latest-version": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "package-json": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lazy-ass": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "> 0.8"
+ }
+ },
+ "node_modules/less": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "copy-anything": "^2.0.1",
+ "parse-node-version": "^1.0.1",
+ "tslib": "^2.3.0"
+ },
+ "bin": {
+ "lessc": "bin/lessc"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "optionalDependencies": {
+ "errno": "^0.1.1",
+ "graceful-fs": "^4.1.2",
+ "image-size": "~0.5.0",
+ "make-dir": "^2.1.0",
+ "mime": "^1.4.1",
+ "needle": "^2.5.2",
+ "source-map": "~0.6.0"
+ }
+ },
+ "node_modules/less-loader": {
+ "version": "10.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "klona": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "less": "^3.5.0 || ^4.0.0",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/less/node_modules/make-dir": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "pify": "^4.0.1",
+ "semver": "^5.6.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/less/node_modules/mime": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/less/node_modules/pify": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/less/node_modules/semver": {
+ "version": "5.7.1",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/less/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/license-webpack-plugin": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "webpack-sources": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack": {
+ "optional": true
+ },
+ "webpack-sources": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2": {
+ "version": "3.14.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^2.1.0",
+ "colorette": "^2.0.16",
+ "log-update": "^4.0.0",
+ "p-map": "^4.0.0",
+ "rfdc": "^1.3.0",
+ "rxjs": "^7.5.1",
+ "through": "^2.3.8",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "enquirer": ">= 2.3.0 < 3"
+ },
+ "peerDependenciesMeta": {
+ "enquirer": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lit": {
+ "version": "2.2.5",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-element": "^3.2.0",
+ "lit-html": "^2.2.0"
+ }
+ },
+ "node_modules/lit-element": {
+ "version": "3.2.0",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@lit/reactive-element": "^1.3.0",
+ "lit-html": "^2.2.0"
+ }
+ },
+ "node_modules/lit-html": {
+ "version": "2.2.5",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@types/trusted-types": "^2.0.2"
+ }
+ },
+ "node_modules/load-json-file": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.1.2",
+ "parse-json": "^4.0.0",
+ "pify": "^3.0.0",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/parse-json": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "error-ex": "^1.3.1",
+ "json-parse-better-errors": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/load-json-file/node_modules/pify": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/loader-runner": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.11.5"
+ }
+ },
+ "node_modules/loader-utils": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "license": "MIT"
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-symbols/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-symbols/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-symbols/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^4.3.0",
+ "cli-cursor": "^3.1.0",
+ "slice-ansi": "^4.0.0",
+ "wrap-ansi": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/log-update/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/log4js": {
+ "version": "6.5.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "date-format": "^4.0.10",
+ "debug": "^4.3.4",
+ "flatted": "^3.2.5",
+ "rfdc": "^1.3.0",
+ "streamroller": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/log4js/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/lowercase-keys": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.25.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sourcemap-codec": "^1.4.4"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-dir/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/make-fetch-happen": {
+ "version": "9.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "agentkeepalive": "^4.1.3",
+ "cacache": "^15.2.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^4.0.1",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^6.0.0",
+ "minipass": "^3.1.3",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^1.3.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.2",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^6.0.0",
+ "ssri": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/map-stream": {
+ "version": "0.1.0",
+ "dev": true
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/memfs": {
+ "version": "3.4.3",
+ "dev": true,
+ "license": "Unlicense",
+ "dependencies": {
+ "fs-monkey": "1.0.3"
+ },
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/memorystream": {
+ "version": "0.3.1",
+ "dev": true,
+ "engines": {
+ "node": ">= 0.10.0"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "2.6.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mini-css-extract-plugin": {
+ "version": "2.5.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "schema-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/mini-css-extract-plugin/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/minimalistic-assert": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minimatch": {
+ "version": "3.0.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.6",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/minipass": {
+ "version": "3.1.6",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-collect": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-fetch": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.1.0",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.12"
+ }
+ },
+ "node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-json-stream": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsonparse": "^1.3.1",
+ "minipass": "^3.0.0"
+ }
+ },
+ "node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-sized": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/modern-normalize": {
+ "version": "1.1.0",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/multicast-dns": {
+ "version": "6.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dns-packet": "^1.3.1",
+ "thunky": "^1.0.2"
+ },
+ "bin": {
+ "multicast-dns": "cli.js"
+ }
+ },
+ "node_modules/multicast-dns-service-types": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mute-stream": {
+ "version": "0.0.8",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.4",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/needle": {
+ "version": "2.9.1",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "debug": "^3.2.6",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ },
+ "bin": {
+ "needle": "bin/needle"
+ },
+ "engines": {
+ "node": ">= 4.4.x"
+ }
+ },
+ "node_modules/needle/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/neo-async": {
+ "version": "2.6.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ng-packagr": {
+ "version": "13.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/plugin-json": "^4.1.0",
+ "@rollup/plugin-node-resolve": "^13.0.0",
+ "ajv": "^8.0.0",
+ "ansi-colors": "^4.1.1",
+ "browserslist": "^4.16.1",
+ "cacache": "^15.0.6",
+ "chokidar": "^3.5.1",
+ "commander": "^8.0.0",
+ "dependency-graph": "^0.11.0",
+ "esbuild-wasm": "^0.14.0",
+ "find-cache-dir": "^3.3.1",
+ "glob": "^7.1.6",
+ "injection-js": "^2.4.0",
+ "jsonc-parser": "^3.0.0",
+ "less": "^4.1.0",
+ "ora": "^5.1.0",
+ "postcss": "^8.2.4",
+ "postcss-preset-env": "^7.0.0",
+ "postcss-url": "^10.1.1",
+ "rollup": "^2.45.1",
+ "rollup-plugin-sourcemaps": "^0.6.3",
+ "rxjs": "^7.0.0",
+ "sass": "^1.32.8",
+ "stylus": "^0.56.0"
+ },
+ "bin": {
+ "ng-packagr": "cli/main.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.15.0 || >=16.10.0"
+ },
+ "optionalDependencies": {
+ "esbuild": "^0.14.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler-cli": "^13.0.0",
+ "tslib": "^2.3.0",
+ "typescript": ">=4.4.0 <4.7"
+ }
+ },
+ "node_modules/ng-packagr/node_modules/commander": {
+ "version": "8.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ngx-clipboard": {
+ "version": "15.1.0",
+ "license": "MIT",
+ "dependencies": {
+ "ngx-window-token": ">=6.0.0",
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=13.0.0",
+ "@angular/core": ">=13.0.0"
+ }
+ },
+ "node_modules/ngx-cookie-service": {
+ "version": "13.2.1",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "^13.0.0",
+ "@angular/core": "^13.0.0"
+ }
+ },
+ "node_modules/ngx-window-token": {
+ "version": "6.0.0",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12.20.x"
+ },
+ "peerDependencies": {
+ "@angular/common": ">=13.0.0",
+ "@angular/core": ">=13.0.0"
+ }
+ },
+ "node_modules/nice-napi": {
+ "version": "1.0.2",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "!win32"
+ ],
+ "dependencies": {
+ "node-addon-api": "^3.0.0",
+ "node-gyp-build": "^4.2.2"
+ }
+ },
+ "node_modules/nice-try": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-addon-api": {
+ "version": "3.2.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-forge": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/node-gyp": {
+ "version": "8.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^9.1.0",
+ "nopt": "^5.0.0",
+ "npmlog": "^6.0.0",
+ "rimraf": "^3.0.2",
+ "semver": "^7.3.5",
+ "tar": "^6.1.2",
+ "which": "^2.0.2"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": ">= 10.12.0"
+ }
+ },
+ "node_modules/node-gyp-build": {
+ "version": "4.4.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "node-gyp-build": "bin.js",
+ "node-gyp-build-optional": "optional.js",
+ "node-gyp-build-test": "build-test.js"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nopt": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "1"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/normalize-package-data": {
+ "version": "2.5.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^2.1.4",
+ "resolve": "^1.10.0",
+ "semver": "2 || 3 || 4 || 5",
+ "validate-npm-package-license": "^3.0.1"
+ }
+ },
+ "node_modules/normalize-package-data/node_modules/hosted-git-info": {
+ "version": "2.8.9",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/normalize-package-data/node_modules/semver": {
+ "version": "5.7.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-range": {
+ "version": "0.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "4.5.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npm-bundled": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-normalize-package-bin": "^1.0.1"
+ }
+ },
+ "node_modules/npm-install-checks": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm-normalize-package-bin": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/npm-package-arg": {
+ "version": "8.1.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^4.0.1",
+ "semver": "^7.3.4",
+ "validate-npm-package-name": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm-packlist": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.6",
+ "ignore-walk": "^4.0.1",
+ "npm-bundled": "^1.1.1",
+ "npm-normalize-package-bin": "^1.0.1"
+ },
+ "bin": {
+ "npm-packlist": "bin/index.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm-pick-manifest": {
+ "version": "6.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-install-checks": "^4.0.0",
+ "npm-normalize-package-bin": "^1.0.1",
+ "npm-package-arg": "^8.1.2",
+ "semver": "^7.3.4"
+ }
+ },
+ "node_modules/npm-registry-fetch": {
+ "version": "12.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "make-fetch-happen": "^10.0.1",
+ "minipass": "^3.1.6",
+ "minipass-fetch": "^1.4.1",
+ "minipass-json-stream": "^1.0.1",
+ "minizlib": "^2.1.2",
+ "npm-package-arg": "^8.1.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/@npmcli/fs": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@gar/promisify": "^1.1.3",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/@npmcli/move-file": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mkdirp": "^1.0.4",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/cacache": {
+ "version": "16.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^2.1.0",
+ "@npmcli/move-file": "^2.0.0",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "glob": "^8.0.1",
+ "infer-owner": "^1.0.4",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "mkdirp": "^1.0.4",
+ "p-map": "^4.0.0",
+ "promise-inflight": "^1.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^9.0.0",
+ "tar": "^6.1.11",
+ "unique-filename": "^1.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/glob": {
+ "version": "8.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^5.0.1",
+ "once": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/lru-cache": {
+ "version": "7.10.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/make-fetch-happen": {
+ "version": "10.1.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "agentkeepalive": "^4.2.1",
+ "cacache": "^16.1.0",
+ "http-cache-semantics": "^4.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.0",
+ "is-lambda": "^1.0.1",
+ "lru-cache": "^7.7.1",
+ "minipass": "^3.1.6",
+ "minipass-collect": "^1.0.2",
+ "minipass-fetch": "^2.0.3",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^0.6.3",
+ "promise-retry": "^2.0.1",
+ "socks-proxy-agent": "^6.1.1",
+ "ssri": "^9.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/make-fetch-happen/node_modules/minipass-fetch": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.1.6",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^2.1.2"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.13"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/minimatch": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/ssri": {
+ "version": "9.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/npm-run-all": {
+ "version": "4.1.5",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "bin": {
+ "npm-run-all": "bin/npm-run-all/index.js",
+ "run-p": "bin/run-p/index.js",
+ "run-s": "bin/run-s/index.js"
+ },
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/cross-spawn": {
+ "version": "6.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nice-try": "^1.0.4",
+ "path-key": "^2.0.1",
+ "semver": "^5.5.0",
+ "shebang-command": "^1.2.0",
+ "which": "^1.2.9"
+ },
+ "engines": {
+ "node": ">=4.8"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/path-key": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/semver": {
+ "version": "5.7.1",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/shebang-command": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/shebang-regex": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-run-all/node_modules/which": {
+ "version": "1.3.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/npmlog": {
+ "version": "6.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "are-we-there-yet": "^3.0.0",
+ "console-control-strings": "^1.1.0",
+ "gauge": "^4.0.3",
+ "set-blocking": "^2.0.0"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16.0.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/nx": {
+ "version": "13.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nrwl/cli": "*"
+ },
+ "bin": {
+ "nx": "bin/nx.js"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.12.1",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-is": {
+ "version": "1.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "define-properties": "^1.1.3",
+ "has-symbols": "^1.0.1",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/obuf": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "8.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.3"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.1.0",
+ "chalk": "^4.1.0",
+ "cli-cursor": "^3.1.0",
+ "cli-spinners": "^2.5.0",
+ "is-interactive": "^1.0.0",
+ "is-unicode-supported": "^0.1.0",
+ "log-symbols": "^4.1.0",
+ "strip-ansi": "^6.0.0",
+ "wcwidth": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/ora/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/ora/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ora/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ora/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/os-tmpdir": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ospath": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/p-cancelable": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "4.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-map": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aggregate-error": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-retry/node_modules/retry": {
+ "version": "0.13.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-json": {
+ "version": "6.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "got": "^9.6.0",
+ "registry-auth-token": "^4.0.0",
+ "registry-url": "^5.0.0",
+ "semver": "^6.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/package-json/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/pacote": {
+ "version": "12.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^2.1.0",
+ "@npmcli/installed-package-contents": "^1.0.6",
+ "@npmcli/promise-spawn": "^1.2.0",
+ "@npmcli/run-script": "^2.0.0",
+ "cacache": "^15.0.5",
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.1.0",
+ "infer-owner": "^1.0.4",
+ "minipass": "^3.1.3",
+ "mkdirp": "^1.0.3",
+ "npm-package-arg": "^8.0.1",
+ "npm-packlist": "^3.0.0",
+ "npm-pick-manifest": "^6.0.0",
+ "npm-registry-fetch": "^12.0.0",
+ "promise-retry": "^2.0.1",
+ "read-package-json-fast": "^2.0.1",
+ "rimraf": "^3.0.2",
+ "ssri": "^8.0.1",
+ "tar": "^6.1.0"
+ },
+ "bin": {
+ "pacote": "lib/bin.js"
+ },
+ "engines": {
+ "node": "^12.13.0 || ^14.15.0 || >=16"
+ }
+ },
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "dev": true,
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "5.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.0.0",
+ "error-ex": "^1.3.1",
+ "json-parse-even-better-errors": "^2.3.0",
+ "lines-and-columns": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse-node-version": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "5.1.1",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/parse5-html-rewriting-stream": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^6.0.1",
+ "parse5-sax-parser": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-html-rewriting-stream/node_modules/parse5": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parse5-sax-parser": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^6.0.1"
+ }
+ },
+ "node_modules/parse5-sax-parser/node_modules/parse5": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.7",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-type": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/pause-stream": {
+ "version": "0.0.11",
+ "dev": true,
+ "license": [
+ "MIT",
+ "Apache2"
+ ],
+ "dependencies": {
+ "through": "~2.3"
+ }
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/performance-now": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.3.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.5",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/piscina": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter-asyncresource": "^1.0.0",
+ "hdr-histogram-js": "^2.0.1",
+ "hdr-histogram-percentiles-obj": "^3.0.0"
+ },
+ "optionalDependencies": {
+ "nice-napi": "^1.0.2"
+ }
+ },
+ "node_modules/pkg-dir": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/portfinder": {
+ "version": "1.0.28",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async": "^2.6.2",
+ "debug": "^3.1.1",
+ "mkdirp": "^0.5.5"
+ },
+ "engines": {
+ "node": ">= 0.12.0"
+ }
+ },
+ "node_modules/portfinder/node_modules/async": {
+ "version": "2.6.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash": "^4.17.14"
+ }
+ },
+ "node_modules/portfinder/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/portfinder/node_modules/mkdirp": {
+ "version": "0.5.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.1.30",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ }
+ },
+ "node_modules/postcss-attribute-case-insensitive": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.2"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.2"
+ }
+ },
+ "node_modules/postcss-color-functional-notation": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-hex-alpha": {
+ "version": "8.0.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-color-rebeccapurple": {
+ "version": "7.0.2",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.3"
+ }
+ },
+ "node_modules/postcss-custom-media": {
+ "version": "8.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-custom-properties": {
+ "version": "12.1.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-custom-selectors": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.2"
+ }
+ },
+ "node_modules/postcss-dir-pseudo-class": {
+ "version": "6.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-double-position-gradients": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-env-function": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-visible": {
+ "version": "6.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-focus-within": {
+ "version": "5.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.9"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-font-variant": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-gap-properties": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-image-set-function": {
+ "version": "4.0.6",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "14.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-initial": {
+ "version": "4.0.1",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-lab-function": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "@csstools/postcss-progressive-custom-properties": "^1.1.0",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-loader": {
+ "version": "6.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cosmiconfig": "^7.0.0",
+ "klona": "^2.0.5",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "postcss": "^7.0.0 || ^8.0.1",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/postcss-logical": {
+ "version": "5.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-media-minmax": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-extract-imports": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-local-by-default": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "icss-utils": "^5.0.0",
+ "postcss-selector-parser": "^6.0.2",
+ "postcss-value-parser": "^4.1.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-scope": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.4"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-modules-values": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "icss-utils": "^5.0.0"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >= 14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-nesting": {
+ "version": "10.1.7",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "@csstools/selector-specificity": "1.0.0",
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-overflow-shorthand": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-page-break": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8"
+ }
+ },
+ "node_modules/postcss-place": {
+ "version": "7.0.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-value-parser": "^4.2.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-preset-env": {
+ "version": "7.2.3",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "autoprefixer": "^10.4.2",
+ "browserslist": "^4.19.1",
+ "caniuse-lite": "^1.0.30001299",
+ "css-blank-pseudo": "^3.0.2",
+ "css-has-pseudo": "^3.0.3",
+ "css-prefers-color-scheme": "^6.0.2",
+ "cssdb": "^5.0.0",
+ "postcss-attribute-case-insensitive": "^5.0.0",
+ "postcss-color-functional-notation": "^4.2.1",
+ "postcss-color-hex-alpha": "^8.0.2",
+ "postcss-color-rebeccapurple": "^7.0.2",
+ "postcss-custom-media": "^8.0.0",
+ "postcss-custom-properties": "^12.1.2",
+ "postcss-custom-selectors": "^6.0.0",
+ "postcss-dir-pseudo-class": "^6.0.3",
+ "postcss-double-position-gradients": "^3.0.4",
+ "postcss-env-function": "^4.0.4",
+ "postcss-focus-visible": "^6.0.3",
+ "postcss-focus-within": "^5.0.3",
+ "postcss-font-variant": "^5.0.0",
+ "postcss-gap-properties": "^3.0.2",
+ "postcss-image-set-function": "^4.0.4",
+ "postcss-initial": "^4.0.1",
+ "postcss-lab-function": "^4.0.3",
+ "postcss-logical": "^5.0.3",
+ "postcss-media-minmax": "^5.0.0",
+ "postcss-nesting": "^10.1.2",
+ "postcss-overflow-shorthand": "^3.0.2",
+ "postcss-page-break": "^3.0.4",
+ "postcss-place": "^7.0.3",
+ "postcss-pseudo-class-any-link": "^7.0.2",
+ "postcss-replace-overflow-wrap": "^4.0.0",
+ "postcss-selector-not": "^5.0.0"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-pseudo-class-any-link": {
+ "version": "7.1.4",
+ "dev": true,
+ "license": "CC0-1.0",
+ "dependencies": {
+ "postcss-selector-parser": "^6.0.10"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >=16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4"
+ }
+ },
+ "node_modules/postcss-replace-overflow-wrap": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "postcss": "^8.0.3"
+ }
+ },
+ "node_modules/postcss-selector-not": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.0.10",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-url": {
+ "version": "10.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "make-dir": "~3.1.0",
+ "mime": "~2.5.2",
+ "minimatch": "~3.0.4",
+ "xxhashjs": "~0.2.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-url/node_modules/mime": {
+ "version": "2.5.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prepend-http": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "2.6.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin-prettier.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/promise-inflight": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/prompts": {
+ "version": "2.4.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kleur": "^3.0.3",
+ "sisteransi": "^1.0.5"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-addr/node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prr": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/ps-tree": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "event-stream": "=3.3.4"
+ },
+ "bin": {
+ "ps-tree": "bin/ps-tree.js"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/psl": {
+ "version": "1.8.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pump": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/pupa": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-goat": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/qjobs": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.9"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.5.3",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/ramda": {
+ "version": "0.28.0",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ramda"
+ }
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.4.24",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/rc": {
+ "version": "1.2.8",
+ "dev": true,
+ "license": "(BSD-2-Clause OR MIT OR Apache-2.0)",
+ "dependencies": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "bin": {
+ "rc": "cli.js"
+ }
+ },
+ "node_modules/rc/node_modules/ini": {
+ "version": "1.3.8",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/rc/node_modules/strip-json-comments": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/read-package-json-fast": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "json-parse-even-better-errors": "^2.3.0",
+ "npm-normalize-package-bin": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "load-json-file": "^4.0.0",
+ "normalize-package-data": "^2.3.2",
+ "path-type": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/read-pkg/node_modules/path-type": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/read-pkg/node_modules/pify": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.1.13",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/regenerate": {
+ "version": "1.4.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regenerator-runtime": {
+ "version": "0.13.9",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regenerator-transform": {
+ "version": "0.15.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.8.4"
+ }
+ },
+ "node_modules/regex-parser": {
+ "version": "2.2.11",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.4.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "functions-have-names": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpp": {
+ "version": "3.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mysticatea"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.0.1",
+ "regjsgen": "^0.6.0",
+ "regjsparser": "^0.8.2",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/registry-auth-token": {
+ "version": "4.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rc": "^1.2.8"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/registry-url": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rc": "^1.2.8"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/regjsgen": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regjsparser": {
+ "version": "0.8.4",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~0.5.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "0.5.0",
+ "dev": true,
+ "bin": {
+ "jsesc": "bin/jsesc"
+ }
+ },
+ "node_modules/request-progress": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "throttleit": "^1.0.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requireindex": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.5"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.8.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/resolve-url-loader": {
+ "version": "5.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "adjust-sourcemap-loader": "^4.0.0",
+ "convert-source-map": "^1.7.0",
+ "loader-utils": "^2.0.0",
+ "postcss": "^8.2.14",
+ "source-map": "0.6.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/resolve-url-loader/node_modules/loader-utils": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "big.js": "^5.2.2",
+ "emojis-list": "^3.0.0",
+ "json5": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=8.9.0"
+ }
+ },
+ "node_modules/resolve-url-loader/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/responselike": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^1.0.0"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "2.74.1",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rollup-plugin-sourcemaps": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^3.0.9",
+ "source-map-resolve": "^0.6.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=10.0.0",
+ "rollup": ">=0.31.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/run-async": {
+ "version": "2.4.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rw": {
+ "version": "1.3.3",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/rxjs": {
+ "version": "7.5.5",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/rxjs-for-await": {
+ "version": "0.0.2",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "rxjs": "^6.0.0"
+ }
+ },
+ "node_modules/rxjs-marbles": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-equals": "^2.0.0",
+ "rxjs-report-usage": "^1.0.4"
+ },
+ "peerDependencies": {
+ "rxjs": "^7.0.0"
+ }
+ },
+ "node_modules/rxjs-report-usage": {
+ "version": "1.0.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.10.3",
+ "@babel/traverse": "^7.10.3",
+ "@babel/types": "^7.10.3",
+ "bent": "~7.3.6",
+ "chalk": "~4.1.0",
+ "glob": "~7.2.0",
+ "prompts": "~2.4.2"
+ },
+ "bin": {
+ "rxjs-report-usage": "bin/rxjs-report-usage"
+ }
+ },
+ "node_modules/rxjs-report-usage/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/rxjs-report-usage/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/rxjs-report-usage/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/rxjs-report-usage/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rxjs-report-usage/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rxjs-report-usage/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "license": "MIT"
+ },
+ "node_modules/sass": {
+ "version": "1.49.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": ">=3.0.0 <4.0.0",
+ "immutable": "^4.0.0",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/sass-loader": {
+ "version": "12.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "klona": "^2.0.4",
+ "neo-async": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "fibers": ">= 3.1.0",
+ "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+ "sass": "^1.3.0",
+ "webpack": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "fibers": {
+ "optional": true
+ },
+ "node-sass": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.2.4",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/schema-utils": {
+ "version": "2.7.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.5",
+ "ajv": "^6.12.4",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 8.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/schema-utils/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/schema-utils/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/select-hose": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/selfsigned": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "node-forge": "^1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.3.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver-diff": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^6.3.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/semver-diff/node_modules/semver": {
+ "version": "6.3.0",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.18.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "0.5.2",
+ "http-errors": "2.0.0",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/send/node_modules/mime": {
+ "version": "1.6.0",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/send/node_modules/statuses": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-index": {
+ "version": "1.9.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "batch": "0.6.1",
+ "debug": "2.6.9",
+ "escape-html": "~1.0.3",
+ "http-errors": "~1.6.2",
+ "mime-types": "~2.1.17",
+ "parseurl": "~1.3.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/serve-index/node_modules/debug": {
+ "version": "2.6.9",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/serve-index/node_modules/depd": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/http-errors": {
+ "version": "1.6.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~1.1.2",
+ "inherits": "2.0.3",
+ "setprototypeof": "1.1.0",
+ "statuses": ">= 1.4.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/serve-index/node_modules/inherits": {
+ "version": "2.0.3",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/serve-index/node_modules/ms": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/serve-index/node_modules/setprototypeof": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/serve-static": {
+ "version": "1.15.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~1.0.2",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "0.18.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-blocking": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shell-quote": {
+ "version": "1.7.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/side-channel": {
+ "version": "1.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.0",
+ "get-intrinsic": "^1.0.2",
+ "object-inspect": "^1.9.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "3.0.7",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/simple-bin-help": {
+ "version": "1.7.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "3.2.7",
+ "update-notifier": "5.1.0",
+ "word-wrap": "1.2.3"
+ }
+ },
+ "node_modules/simple-bin-help/node_modules/debug": {
+ "version": "3.2.7",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/sisteransi": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/slash": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "debug": "~4.3.2",
+ "engine.io": "~6.2.0",
+ "socket.io-adapter": "~2.4.0",
+ "socket.io-parser": "~4.0.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.4.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/component-emitter": "^1.2.10",
+ "component-emitter": "~1.3.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/sockjs": {
+ "version": "0.3.24",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "faye-websocket": "^0.11.3",
+ "uuid": "^8.3.2",
+ "websocket-driver": "^0.7.4"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.6.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip": "^1.1.5",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.13.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^6.0.2",
+ "debug": "^4.3.3",
+ "socks": "^2.6.2"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.3",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-loader": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abab": "^2.0.5",
+ "iconv-lite": "^0.6.3",
+ "source-map-js": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/source-map-loader/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-resolve": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "atob": "^2.1.2",
+ "decode-uri-component": "^0.2.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/sourcemap-codec": {
+ "version": "1.4.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.11",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/spdy": {
+ "version": "4.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.0",
+ "handle-thing": "^2.0.0",
+ "http-deceiver": "^1.2.7",
+ "select-hose": "^2.0.0",
+ "spdy-transport": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/spdy-transport": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.0",
+ "detect-node": "^2.0.4",
+ "hpack.js": "^2.1.6",
+ "obuf": "^1.1.2",
+ "readable-stream": "^3.0.6",
+ "wbuf": "^1.7.3"
+ }
+ },
+ "node_modules/split": {
+ "version": "0.3.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "through": "2"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/sshpk": {
+ "version": "1.17.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asn1": "~0.2.3",
+ "assert-plus": "^1.0.0",
+ "bcrypt-pbkdf": "^1.0.0",
+ "dashdash": "^1.12.0",
+ "ecc-jsbn": "~0.1.1",
+ "getpass": "^0.1.1",
+ "jsbn": "~0.1.0",
+ "safer-buffer": "^2.0.2",
+ "tweetnacl": "~0.14.0"
+ },
+ "bin": {
+ "sshpk-conv": "bin/sshpk-conv",
+ "sshpk-sign": "bin/sshpk-sign",
+ "sshpk-verify": "bin/sshpk-verify"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ssri": {
+ "version": "8.0.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.1.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/start-server-and-test": {
+ "version": "1.14.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bluebird": "3.7.2",
+ "check-more-types": "2.24.0",
+ "debug": "4.3.2",
+ "execa": "5.1.1",
+ "lazy-ass": "1.6.0",
+ "ps-tree": "1.2.0",
+ "wait-on": "6.0.0"
+ },
+ "bin": {
+ "server-test": "src/bin/start.js",
+ "start-server-and-test": "src/bin/start.js",
+ "start-test": "src/bin/start.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/start-server-and-test/node_modules/debug": {
+ "version": "4.3.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/start-server-and-test/node_modules/execa": {
+ "version": "5.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^6.0.0",
+ "human-signals": "^2.1.0",
+ "is-stream": "^2.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^4.0.1",
+ "onetime": "^5.1.2",
+ "signal-exit": "^3.0.3",
+ "strip-final-newline": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/start-server-and-test/node_modules/get-stream": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/start-server-and-test/node_modules/human-signals": {
+ "version": "2.1.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10.17.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "1.5.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/stream-combiner": {
+ "version": "0.0.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "duplexer": "~0.1.1"
+ }
+ },
+ "node_modules/streamroller": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "date-format": "^4.0.10",
+ "debug": "^4.3.4",
+ "fs-extra": "^10.1.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/streamroller/node_modules/debug": {
+ "version": "4.3.4",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/streamroller/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string.prototype.padend": {
+ "version": "3.1.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.19.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "define-properties": "^1.1.4",
+ "es-abstract": "^1.19.5"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/stylus": {
+ "version": "0.56.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "css": "^3.0.0",
+ "debug": "^4.3.2",
+ "glob": "^7.1.6",
+ "safer-buffer": "^2.1.2",
+ "sax": "~1.2.4",
+ "source-map": "^0.7.3"
+ },
+ "bin": {
+ "stylus": "bin/stylus"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/stylus-loader": {
+ "version": "6.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-glob": "^3.2.7",
+ "klona": "^2.0.4",
+ "normalize-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "stylus": ">=0.52.4",
+ "webpack": "^5.0.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "5.5.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/symbol-observable": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/tapable": {
+ "version": "2.2.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.1.11",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^3.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bl": "^4.0.3",
+ "end-of-stream": "^1.4.1",
+ "fs-constants": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.11.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.5.0",
+ "commander": "^2.20.0",
+ "source-map": "~0.7.2",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser-webpack-plugin": {
+ "version": "5.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jest-worker": "^27.4.5",
+ "schema-utils": "^3.1.1",
+ "serialize-javascript": "^6.0.0",
+ "source-map": "^0.6.1",
+ "terser": "^5.7.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^5.1.0"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "esbuild": {
+ "optional": true
+ },
+ "uglify-js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/terser-webpack-plugin/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/terser-webpack-plugin/node_modules/source-map": {
+ "version": "0.6.1",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/test-exclude": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@istanbuljs/schema": "^0.1.2",
+ "glob": "^7.1.4",
+ "minimatch": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/text-table": {
+ "version": "0.2.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/throttleit": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/through": {
+ "version": "2.3.8",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/thunky": {
+ "version": "1.1.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tmp": {
+ "version": "0.2.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rimraf": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8.17.0"
+ }
+ },
+ "node_modules/to-fast-properties": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/to-readable-stream": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tough-cookie": {
+ "version": "2.5.0",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "psl": "^1.1.28",
+ "punycode": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/transformation-matrix": {
+ "version": "1.15.3",
+ "license": "MIT"
+ },
+ "node_modules/tree-kill": {
+ "version": "1.2.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tree-kill": "cli.js"
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "3.14.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/tsconfig-paths/node_modules/json5": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.3.1",
+ "license": "0BSD"
+ },
+ "node_modules/tsutils": {
+ "version": "3.21.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^1.8.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ },
+ "peerDependencies": {
+ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta"
+ }
+ },
+ "node_modules/tsutils-etc": {
+ "version": "1.4.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/yargs": "^17.0.0",
+ "yargs": "^17.0.0"
+ },
+ "bin": {
+ "ts-flags": "bin/ts-flags",
+ "ts-kind": "bin/ts-kind"
+ },
+ "peerDependencies": {
+ "tsutils": "^3.0.0",
+ "typescript": "^4.0.0"
+ }
+ },
+ "node_modules/tsutils/node_modules/tslib": {
+ "version": "1.14.1",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/tunnel-agent": {
+ "version": "0.6.0",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/tv4": {
+ "version": "1.3.0",
+ "dev": true,
+ "license": [
+ {
+ "type": "Public Domain",
+ "url": "http://geraintluff.github.io/tv4/LICENSE.txt"
+ },
+ {
+ "type": "MIT",
+ "url": "http://jsonary.com/LICENSE.txt"
+ }
+ ],
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/tweetnacl": {
+ "version": "0.14.5",
+ "dev": true,
+ "license": "Unlicense"
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typed-assert": {
+ "version": "1.0.9",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/typedarray-to-buffer": {
+ "version": "3.1.5",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-typedarray": "^1.0.0"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "4.6.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=4.2.0"
+ }
+ },
+ "node_modules/ua-parser-js": {
+ "version": "0.7.31",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.2",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.0.3",
+ "which-boxed-primitive": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-ecmascript": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unique-filename": {
+ "version": "1.1.1",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "unique-slug": "^2.0.0"
+ }
+ },
+ "node_modules/unique-slug": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ }
+ },
+ "node_modules/unique-string": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crypto-random-string": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/untildify": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/update-notifier": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boxen": "^5.0.0",
+ "chalk": "^4.1.0",
+ "configstore": "^5.0.1",
+ "has-yarn": "^2.1.0",
+ "import-lazy": "^2.1.0",
+ "is-ci": "^2.0.0",
+ "is-installed-globally": "^0.4.0",
+ "is-npm": "^5.0.0",
+ "is-yarn-global": "^0.3.0",
+ "latest-version": "^5.1.0",
+ "pupa": "^2.1.1",
+ "semver": "^7.3.4",
+ "semver-diff": "^3.1.1",
+ "xdg-basedir": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/yeoman/update-notifier?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/chalk": {
+ "version": "4.1.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/update-notifier/node_modules/ci-info": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-notifier/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/update-notifier/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/update-notifier/node_modules/has-flag": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/update-notifier/node_modules/is-ci": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^2.0.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/update-notifier/node_modules/supports-color": {
+ "version": "7.2.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/url-parse-lax": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prepend-http": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/v8-compile-cache": {
+ "version": "2.3.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/validate-npm-package-name": {
+ "version": "3.0.0",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "builtins": "^1.0.3"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.0",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wait-on": {
+ "version": "6.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "axios": "^0.21.1",
+ "joi": "^17.4.0",
+ "lodash": "^4.17.21",
+ "minimist": "^1.2.5",
+ "rxjs": "^7.1.0"
+ },
+ "bin": {
+ "wait-on": "bin/wait-on"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/watchpack": {
+ "version": "2.3.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/wbuf": {
+ "version": "1.7.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimalistic-assert": "^1.0.0"
+ }
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/webcola": {
+ "version": "3.4.0",
+ "license": "MIT",
+ "dependencies": {
+ "d3-dispatch": "^1.0.3",
+ "d3-drag": "^1.0.4",
+ "d3-shape": "^1.3.5",
+ "d3-timer": "^1.0.5"
+ }
+ },
+ "node_modules/webpack": {
+ "version": "5.70.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint-scope": "^3.7.3",
+ "@types/estree": "^0.0.51",
+ "@webassemblyjs/ast": "1.11.1",
+ "@webassemblyjs/wasm-edit": "1.11.1",
+ "@webassemblyjs/wasm-parser": "1.11.1",
+ "acorn": "^8.4.1",
+ "acorn-import-assertions": "^1.7.6",
+ "browserslist": "^4.14.5",
+ "chrome-trace-event": "^1.0.2",
+ "enhanced-resolve": "^5.9.2",
+ "es-module-lexer": "^0.9.0",
+ "eslint-scope": "5.1.1",
+ "events": "^3.2.0",
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.2.9",
+ "json-parse-better-errors": "^1.0.2",
+ "loader-runner": "^4.2.0",
+ "mime-types": "^2.1.27",
+ "neo-async": "^2.6.2",
+ "schema-utils": "^3.1.0",
+ "tapable": "^2.1.1",
+ "terser-webpack-plugin": "^5.1.3",
+ "watchpack": "^2.3.1",
+ "webpack-sources": "^3.2.3"
+ },
+ "bin": {
+ "webpack": "bin/webpack.js"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-dev-middleware": {
+ "version": "5.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "colorette": "^2.0.10",
+ "memfs": "^3.2.2",
+ "mime-types": "^2.1.31",
+ "range-parser": "^1.2.1",
+ "schema-utils": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ },
+ "peerDependencies": {
+ "webpack": "^4.0.0 || ^5.0.0"
+ }
+ },
+ "node_modules/webpack-dev-middleware/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/webpack-dev-middleware/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/webpack-dev-server": {
+ "version": "4.7.3",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/bonjour": "^3.5.9",
+ "@types/connect-history-api-fallback": "^1.3.5",
+ "@types/serve-index": "^1.9.1",
+ "@types/sockjs": "^0.3.33",
+ "@types/ws": "^8.2.2",
+ "ansi-html-community": "^0.0.8",
+ "bonjour": "^3.5.0",
+ "chokidar": "^3.5.2",
+ "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",
+ "ipaddr.js": "^2.0.1",
+ "open": "^8.0.9",
+ "p-retry": "^4.5.0",
+ "portfinder": "^1.0.28",
+ "schema-utils": "^4.0.0",
+ "selfsigned": "^2.0.0",
+ "serve-index": "^1.9.1",
+ "sockjs": "^0.3.21",
+ "spdy": "^4.0.2",
+ "strip-ansi": "^7.0.0",
+ "webpack-dev-middleware": "^5.3.0",
+ "ws": "^8.1.0"
+ },
+ "bin": {
+ "webpack-dev-server": "bin/webpack-dev-server.js"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "peerDependencies": {
+ "webpack": "^4.37.0 || ^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "webpack-cli": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ajv-keywords": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3"
+ },
+ "peerDependencies": {
+ "ajv": "^8.8.2"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/schema-utils": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.9",
+ "ajv": "^8.8.0",
+ "ajv-formats": "^2.1.1",
+ "ajv-keywords": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 12.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/strip-ansi": {
+ "version": "7.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/webpack-dev-server/node_modules/ws": {
+ "version": "8.7.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "5.8.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "wildcard": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/webpack-sources": {
+ "version": "3.2.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/webpack-subresource-integrity": {
+ "version": "5.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "typed-assert": "^1.0.8"
+ },
+ "engines": {
+ "node": ">= 12"
+ },
+ "peerDependencies": {
+ "html-webpack-plugin": ">= 5.0.0-beta.1 < 6",
+ "webpack": "^5.12.0"
+ },
+ "peerDependenciesMeta": {
+ "html-webpack-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/webpack/node_modules/@types/estree": {
+ "version": "0.0.51",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/webpack/node_modules/ajv": {
+ "version": "6.12.6",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/webpack/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/webpack/node_modules/schema-utils": {
+ "version": "3.1.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.8",
+ "ajv": "^6.12.5",
+ "ajv-keywords": "^3.5.2"
+ },
+ "engines": {
+ "node": ">= 10.13.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/which-boxed-primitive": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.0.1",
+ "is-boolean-object": "^1.1.0",
+ "is-number-object": "^1.0.4",
+ "is-string": "^1.0.5",
+ "is-symbol": "^1.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/wide-align": {
+ "version": "1.1.5",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^1.0.2 || 2 || 3 || 4"
+ }
+ },
+ "node_modules/widest-line": {
+ "version": "3.1.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "string-width": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.0",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.3",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-convert": {
+ "version": "2.0.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/color-name": {
+ "version": "1.1.4",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/write-file-atomic": {
+ "version": "3.0.3",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4",
+ "is-typedarray": "^1.0.0",
+ "signal-exit": "^3.0.2",
+ "typedarray-to-buffer": "^3.1.5"
+ }
+ },
+ "node_modules/ws": {
+ "version": "7.5.8",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.3.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": "^5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xdg-basedir": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "12.0.0",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0"
+ }
+ },
+ "node_modules/xxhashjs": {
+ "version": "0.2.2",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cuint": "^0.2.2"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "1.10.2",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.5.1",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^7.0.2",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "20.0.0",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yargs/node_modules/yargs-parser": {
+ "version": "21.0.1",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/zone.js": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.7.tgz",
+ "integrity": "sha512-e39K2EdK5JfA3FDuUTVRvPlYV4aBfnOOcGuILhQAT7nzeV12uSrLBzImUM9CDVoncDSX4brR/gwqu0heQ3BQ0g==",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ }
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/package.json b/projects/frontend/shared-components/gui/package.json
new file mode 100644
index 0000000000..e605fa413f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/package.json
@@ -0,0 +1,106 @@
+{
+ "name": "shared-wrapper",
+ "version": "0.0.1",
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": ">=8.5.5"
+ },
+ "private": true,
+ "scripts": {
+ "ng": "node --max_old_space_size=2048 ./node_modules/@angular/cli/bin/ng",
+ "start": "npm run ng -- serve",
+ "build": "npm run ng -- build",
+ "build:dev": "npm run ng -- build --configuration=development",
+ "build:watch": "npm run build -- --watch",
+ "build:documentation": "npm run build -- documentation-ui --configuration=production",
+ "test": "npm run ng -- test --watch=false",
+ "test:headless": "npm run test -- --browsers=ChromeHeadless_No_Sandbox",
+ "lint": "npm run ng -- lint",
+ "prettier-format": "prettier --config .prettierrc.json 'projects/**/src/**/*.ts' --write",
+ "clean:dist": "rimraf dist",
+ "clean:reports": "rimraf reports",
+ "clean": "npm-run-all --parallel clean:*",
+ "setup": "npm ci --omit=optional"
+ },
+ "dependencies": {
+ "@angular/animations": "13.3.10",
+ "@angular/cdk": "13.3.8",
+ "@angular/common": "13.3.10",
+ "@angular/compiler": "13.3.10",
+ "@angular/core": "13.3.10",
+ "@angular/forms": "13.3.10",
+ "@angular/platform-browser": "13.3.10",
+ "@angular/platform-browser-dynamic": "13.3.10",
+ "@angular/router": "13.3.10",
+ "@cds/core": "5.7.2",
+ "@clr/angular": "13.3.1",
+ "@clr/icons": "13.0.2",
+ "@clr/ui": "13.3.1",
+ "@ngrx/effects": "13.2.0",
+ "@ngrx/router-store": "13.2.0",
+ "@ngrx/store": "13.2.0",
+ "@swimlane/ngx-graph": "8.0.0",
+ "@webcomponents/custom-elements": "1.5.0",
+ "angular-ng-autocomplete": "2.0.8",
+ "d3": "5.16.0",
+ "dompurify": "2.3.8",
+ "lodash": "4.17.21",
+ "ngx-clipboard": "15.1.0",
+ "ngx-cookie-service": "13.2.1",
+ "rxjs": "7.5.5",
+ "tslib": "2.3.1",
+ "zone.js": "0.11.7"
+ },
+ "optionalDependencies": {
+ "@vdk/shared": "0.0.0"
+ },
+ "devDependencies": {
+ "@angular-devkit/build-angular": "13.3.7",
+ "@angular-devkit/schematics": "13.3.7",
+ "@angular-eslint/builder": "13.2.1",
+ "@angular-eslint/eslint-plugin": "13.2.1",
+ "@angular-eslint/eslint-plugin-template": "13.2.1",
+ "@angular-eslint/schematics": "13.2.1",
+ "@angular-eslint/template-parser": "13.2.1",
+ "@angular/cli": "13.3.7",
+ "@angular/compiler-cli": "13.3.10",
+ "@neuralegion/cypress-har-generator": "5.3.0",
+ "@ngrx/schematics": "13.2.0",
+ "@ngrx/store-devtools": "13.2.0",
+ "@types/jasmine": "4.0.3",
+ "@types/lodash": "4.14.182",
+ "@types/node": "17.0.35",
+ "@typescript-eslint/eslint-plugin": "5.26.0",
+ "@typescript-eslint/parser": "5.26.0",
+ "angular-oauth2-oidc": "13.0.1",
+ "cypress": "9.7.0",
+ "cypress-grep": "2.13.1",
+ "cypress-localstorage-commands": "1.7.0",
+ "cypress-terminal-report": "3.5.2",
+ "eslint": "8.16.0",
+ "eslint-import-resolver-typescript": "2.7.1",
+ "eslint-plugin-cypress": "2.12.1",
+ "eslint-plugin-import": "2.26.0",
+ "eslint-plugin-jasmine": "4.1.3",
+ "eslint-plugin-jsdoc": "39.3.2",
+ "eslint-plugin-ngrx": "2.1.2",
+ "eslint-plugin-prefer-arrow": "1.2.3",
+ "eslint-plugin-prettier": "4.0.0",
+ "eslint-plugin-rxjs": "5.0.2",
+ "jasmine-core": "4.1.1",
+ "jasmine-spec-reporter": "7.0.0",
+ "karma": "6.3.20",
+ "karma-chrome-launcher": "3.1.1",
+ "karma-coverage": "2.2.0",
+ "karma-jasmine": "5.0.1",
+ "karma-jasmine-html-reporter": "1.7.0",
+ "karma-junit-reporter": "2.0.1",
+ "ng-packagr": "13.3.1",
+ "npm-run-all": "4.1.5",
+ "prettier": "2.6.2",
+ "rimraf": "3.0.2",
+ "rxjs-marbles": "7.0.1",
+ "start-server-and-test": "1.14.0",
+ "typescript": "4.6.4"
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/.browserslistrc b/projects/frontend/shared-components/gui/projects/documentation-ui/.browserslistrc
new file mode 100644
index 0000000000..427441dc93
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/.browserslistrc
@@ -0,0 +1,17 @@
+# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
+# For additional information regarding the format and rule options, please see:
+# https://github.com/browserslist/browserslist#queries
+
+# For the full list of supported browsers by the Angular framework, please see:
+# https://angular.io/guide/browser-support
+
+# You can see what browsers were selected by your queries by running:
+# npx browserslist
+
+last 1 Chrome version
+last 1 Firefox version
+last 2 Edge major versions
+last 2 Safari major versions
+last 2 iOS major versions
+Firefox ESR
+not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/.eslintrc.json b/projects/frontend/shared-components/gui/projects/documentation-ui/.eslintrc.json
new file mode 100644
index 0000000000..6df02389b9
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/.eslintrc.json
@@ -0,0 +1,87 @@
+{
+ "extends": "../../.eslintrc.json",
+ "ignorePatterns": [
+ "!**/*"
+ ],
+ "overrides": [
+ {
+ "files": [
+ "*.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "projects/documentation-ui/tsconfig.app.json"
+ ],
+ "createDefaultProgram": true
+ },
+ "rules": {
+ "@angular-eslint/component-selector": [
+ "error",
+ {
+ "type": "element",
+ "prefix": "app",
+ "style": "kebab-case"
+ }
+ ],
+ "@angular-eslint/directive-selector": [
+ "error",
+ {
+ "type": "attribute",
+ "prefix": "app",
+ "style": "camelCase"
+ }
+ ],
+ "@typescript-eslint/naming-convention": [
+ "warn",
+ {
+ "selector": "property",
+ "format": [
+ "camelCase"
+ ],
+ "leadingUnderscore": "allow",
+ "filter": {
+ "regex": "^((\\$\\..+))$",
+ "match": false
+ }
+ },
+ {
+ "selector": "classProperty",
+ "modifiers": [
+ "static"
+ ],
+ "format": [
+ "camelCase",
+ "UPPER_CASE"
+ ],
+ "leadingUnderscore": "allow"
+ }
+ ],
+ "@typescript-eslint/no-shadow": "error",
+ "no-shadow": "off"
+ }
+ },
+ {
+ "files": [
+ "*.spec.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "projects/shared/tsconfig.spec.json"
+ ]
+ },
+ "rules": {
+ "@typescript-eslint/ban-ts-comment": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-floating-promises": "off",
+ "@typescript-eslint/no-unsafe-assignment": "off",
+ "@typescript-eslint/unbound-method": "off"
+ }
+ },
+ {
+ "files": [
+ "*.html"
+ ],
+ "rules": {}
+ }
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/karma.conf.js b/projects/frontend/shared-components/gui/projects/documentation-ui/karma.conf.js
new file mode 100644
index 0000000000..7b134b5021
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/karma.conf.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function(config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-junit-reporter'),
+ require('karma-coverage'),
+ require('@angular-devkit/build-angular/plugins/karma')
+ ],
+ client: {
+ jasmine: {
+ // you can add configuration options for Jasmine here
+ // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
+ // for example, you can disable the random execution with `random: false`
+ // or set a specific seed with `seed: 4321`
+ },
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ coverageReporter: {
+ dir: require('path').join(__dirname, '../../reports/coverage/documentation-ui'),
+ subdir: '.',
+ reporters: [
+ //Code coverage - output only in HTML file
+ { type: 'html' },
+ { type: 'text-summary' },
+ { type: 'lcovonly' }
+ ],
+ check: {
+ global: {
+ lines: 20
+ }
+ }
+ },
+ jasmineHtmlReporter: {
+ suppressAll: true // removes the duplicated traces
+ },
+ junitReporter: {
+ outputDir: require('path').join(__dirname, '../../reports/test-results/documentation-ui'),
+ outputFile: 'unit-tests.xml',
+ useBrowserName: false
+ },
+ reporters: ['progress', 'junit', 'coverage'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['ChromeHeadless'],
+ customLaunchers: {
+ ChromeHeadless_No_Sandbox: {
+ base: 'ChromeHeadless',
+ flags: ['--no-sandbox']
+ }
+ },
+ singleRun: false,
+ restartOnFileChange: true
+ });
+};
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app-routing.module.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app-routing.module.ts
new file mode 100644
index 0000000000..8b2fe0e3e3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app-routing.module.ts
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+ {
+ path: '',
+ redirectTo: 'overview',
+ pathMatch: 'full'
+ },
+ // Overview
+ {
+ path: 'overview',
+ loadChildren: () => import('./core/core.module').then((m) => m.CoreModule)
+ },
+ { path: '**', redirectTo: 'overview' }
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule {
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.html b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.html
new file mode 100644
index 0000000000..37b1d7f209
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.html
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+
+
+
+ Overview
+
+
+ Components
+
+
+
+
+
+
+
+
+
+
+
+
+
Loading Shared Components Documentation UIs
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.scss b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.scss
new file mode 100644
index 0000000000..1afbdcdb34
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.scss
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.main-container {
+ .content-container {
+ .content-area {
+ .csp-content-area-margin {
+ margin: 0 30px;
+ padding-bottom: 30px;
+ height: 100%;
+ }
+ }
+ }
+}
+
+::ng-deep .table-documentation {
+ &.table th,
+ &.table td {
+ text-align: left;
+ }
+}
+
+.checking-user {
+ display: table;
+ height: 26.5rem;
+ width: 100%;
+
+ .loading-spinner {
+ text-align: center;
+ display: table-cell;
+ vertical-align: middle;
+
+ .loading-title {
+ margin-bottom: .6rem;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.spec.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.spec.ts
new file mode 100644
index 0000000000..bd83efc951
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.spec.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { AppComponent } from './app.component';
+import { OAuthModule } from 'angular-oauth2-oidc';
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+
+describe('AppComponent', () => {
+ let component: AppComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [AppComponent],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ imports: [OAuthModule.forRoot(), HttpClientTestingModule]
+ })
+ .compileComponents()
+ .then(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ component = fixture.debugElement.componentInstance;
+ });
+ });
+
+ it('should create the app', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.ts
new file mode 100644
index 0000000000..7e855baa9f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.component.ts
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component } from '@angular/core';
+import { OAuthService } from 'angular-oauth2-oidc';
+import { authCodeFlowConfig } from './auth';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.scss']
+})
+export class AppComponent {
+ constructor(private oauthService: OAuthService) {
+ this.oauthService.configure(authCodeFlowConfig);
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
+ this.oauthService.loadDiscoveryDocumentAndLogin();
+ }
+
+ logout(): void {
+ this.oauthService.logOut();
+ }
+
+ get idToken(): string {
+ return this.oauthService.getIdToken();
+ }
+
+ get userName(): string {
+ return (this.oauthService.getIdentityClaims()) ? this.getIdentityClaim('username') : 'N/A';
+ }
+
+ private getIdentityClaim(userNamePropName: string): string {
+ return this.oauthService.getIdentityClaims()[userNamePropName] as string;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.module.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.module.ts
new file mode 100644
index 0000000000..4a7e36c41d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/app.module.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
+import { BrowserModule } from '@angular/platform-browser';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+import { AuthConfig, OAuthModule, OAuthStorage } from 'angular-oauth2-oidc';
+
+import { ClarityModule } from '@clr/angular';
+
+import { VdkComponentsModule } from '@vdk/shared';
+
+import { TaurusSharedCoreModule, TaurusSharedFeaturesModule, TaurusSharedNgRxModule } from '@vdk/shared';
+
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+import { AuthorizationInterceptor } from './http.interceptor';
+import { authCodeFlowConfig } from './auth';
+
+@NgModule({
+ imports: [
+ AppRoutingModule,
+ BrowserModule,
+ ClarityModule,
+ BrowserAnimationsModule,
+ TaurusSharedCoreModule.forRoot(),
+ TaurusSharedFeaturesModule.forRoot(),
+ TaurusSharedNgRxModule.forRootWithDevtools(),
+ VdkComponentsModule.forRoot(),
+ HttpClientModule,
+ OAuthModule.forRoot({
+ resourceServer: {
+ allowedUrls: [
+ authCodeFlowConfig.issuer,
+ '/metadata'
+ ],
+ sendAccessToken: true
+ }
+ })
+ ],
+ declarations: [AppComponent],
+ providers: [
+ {
+ provide: OAuthStorage,
+ useValue: localStorage
+ },
+ {
+ provide: AuthConfig,
+ useValue: authCodeFlowConfig
+ },
+ {
+ provide: HTTP_INTERCEPTORS,
+ useClass: AuthorizationInterceptor,
+ multi: true
+ }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule {
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/auth.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/auth.ts
new file mode 100644
index 0000000000..48fec45ef1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/auth.ts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { AuthConfig } from 'angular-oauth2-oidc';
+
+export const authCodeFlowConfig: AuthConfig = {
+ issuer: 'https://console-stg.cloud.vmware.com/csp/gateway/am/api/',
+ redirectUri: window.location.origin + '/index.html',
+ skipIssuerCheck: true,
+ requestAccessToken: true,
+ oidc: true,
+ strictDiscoveryDocumentValidation: false,
+ clientId: 'Lt44bhN5yMowdEHuxO3v1SBDKsS3aXW4GcJ',
+ responseType: 'code',
+ scope: 'openid ALL_PERMISSIONS customer_number group_names',
+ showDebugInformation: true,
+ silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
+ useSilentRefresh: true, // Needed for Code Flow to suggest using iframe-based refreshes
+ silentRefreshTimeout: 5000, // For faster testing
+ timeoutFactor: 0.25, // For faster testing
+ sessionChecksEnabled: true,
+ clearHashAfterLogin: false, // https://github.com/manfredsteyer/angular-oauth2-oidc/issues/457#issuecomment-431807040,
+ nonceStateSeparator: 'semicolon' // Real semicolon gets mangled by IdentityServer's URI encoding
+};
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.html b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.html
new file mode 100644
index 0000000000..4c09e4db07
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.html
@@ -0,0 +1,12 @@
+
+
+
+
Shared Components Overview
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.scss b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.scss
new file mode 100644
index 0000000000..836dffb8dc
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.scss
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.core-component {
+ .card {
+ height: 100%;
+
+ .card-block {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ img {
+ width: 100%;
+ }
+ }
+ }
+
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.spec.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.spec.ts
new file mode 100644
index 0000000000..e2d1536521
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.spec.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { CoreComponent } from './core.component';
+
+describe('CoreComponent', () => {
+ let component: CoreComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [CoreComponent]
+ }).compileComponents();
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CoreComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.ts
new file mode 100644
index 0000000000..cd79f06ad7
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.component.ts
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-core',
+ templateUrl: './core.component.html',
+ styleUrls: ['./core.component.scss']
+})
+export class CoreComponent {
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.module.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.module.ts
new file mode 100644
index 0000000000..dd3e55220a
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/core/core.module.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CoreComponent } from './core.component';
+import { RouterModule, Routes } from '@angular/router';
+import { ClarityModule } from '@clr/angular';
+
+const routes: Routes = [
+ {
+ path: '',
+ component: CoreComponent
+ }
+];
+
+@NgModule({
+ declarations: [CoreComponent],
+ imports: [RouterModule.forChild(routes), CommonModule, ClarityModule]
+})
+export class CoreModule {
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/http.interceptor.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/http.interceptor.ts
new file mode 100644
index 0000000000..936095a5e8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/app/http.interceptor.ts
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable, Optional } from '@angular/core';
+import { OAuthModuleConfig, OAuthResourceServerErrorHandler, OAuthStorage } from 'angular-oauth2-oidc';
+import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
+
+import { Observable } from 'rxjs';
+import { authCodeFlowConfig } from './auth';
+
+@Injectable()
+export class AuthorizationInterceptor implements HttpInterceptor {
+
+ constructor(
+ private authStorage: OAuthStorage,
+ private errorHandler: OAuthResourceServerErrorHandler,
+ @Optional() private moduleConfig: OAuthModuleConfig) {
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ public intercept(req: HttpRequest, next: HttpHandler): Observable> {
+ const url = req.url.toLowerCase();
+ if (!this.moduleConfig) {
+ return next.handle(req);
+ }
+ if (!this.moduleConfig.resourceServer) {
+ return next.handle(req);
+ }
+ if (!this.moduleConfig.resourceServer.allowedUrls) {
+ return next.handle(req);
+ }
+ if (!this.checkUrl(url)) {
+ return next.handle(req);
+ }
+
+ const sendAccessToken = this.moduleConfig.resourceServer.sendAccessToken;
+
+ if (sendAccessToken && url.startsWith(authCodeFlowConfig.issuer)) {
+
+ const headers = req.headers
+ .set('Authorization', 'Basic ' + btoa(authCodeFlowConfig.clientId + ':'));
+ req = req.clone({ headers });
+
+ } else if (sendAccessToken) {
+
+ const token = this.authStorage.getItem('access_token');
+ const header = 'Bearer ' + token;
+ const headers = req.headers
+ .set('Authorization', header);
+ req = req.clone({ headers });
+
+ }
+
+ return next.handle(req);
+ }
+
+ private checkUrl(url: string): boolean {
+ const found = this.moduleConfig.resourceServer.allowedUrls.find(u => url.startsWith(u));
+ return !!found;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/assets/.gitkeep b/projects/frontend/shared-components/gui/projects/documentation-ui/src/assets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.prod.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.prod.ts
new file mode 100644
index 0000000000..bdc8e7c053
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.prod.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const environment = {
+ production: true
+};
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.ts
new file mode 100644
index 0000000000..6bf2099149
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/environments/environment.ts
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build --prod` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+ production: 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/projects/frontend/shared-components/gui/projects/documentation-ui/src/favicon.ico b/projects/frontend/shared-components/gui/projects/documentation-ui/src/favicon.ico
new file mode 100644
index 0000000000..997406ad22
Binary files /dev/null and b/projects/frontend/shared-components/gui/projects/documentation-ui/src/favicon.ico differ
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/index.html b/projects/frontend/shared-components/gui/projects/documentation-ui/src/index.html
new file mode 100644
index 0000000000..daccc8173e
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/index.html
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ DocumentationUi
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/main.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/main.ts
new file mode 100644
index 0000000000..34f515dc75
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/main.ts
@@ -0,0 +1,18 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+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();
+}
+
+platformBrowserDynamic()
+ .bootstrapModule(AppModule)
+ .catch((err) => console.error(err));
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/polyfills.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/polyfills.ts
new file mode 100644
index 0000000000..ebd6d2cbc1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/polyfills.ts
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+import '@webcomponents/custom-elements';
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ * with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ * (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js'; // Included with Angular CLI.
+(window as any).global = window;
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/styles.scss b/projects/frontend/shared-components/gui/projects/documentation-ui/src/styles.scss
new file mode 100644
index 0000000000..7bc758add5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/styles.scss
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* You can add global styles to this file, and also import other style files */
+
+/**
+ Placeholder
+ **/
+
+:root {
+ --taurus-placeholder-color: var(--clr-color-neutral-500, #b3b3b3);
+}
+
+/* _normalize overwrite */
+::-webkit-input-placeholder {
+ /* clr-forms-placeholder-color */
+ color: var(--taurus-placeholder-color) !important;
+}
+
+/* Internet Explorer 10-11 */
+:-ms-input-placeholder {
+ /* clr-forms-placeholder-color */
+ color: var(--taurus-placeholder-color) !important;
+}
+
+/* Microsoft Edge */
+::-ms-input-placeholder {
+ /* clr-forms-placeholder-color */
+ color: var(--taurus-placeholder-color) !important;
+}
+
+/* Chrome, Firefox, Opera, Safari 10.1+ */
+::placeholder {
+ /* clr-forms-placeholder-color */
+ color: var(--taurus-placeholder-color) !important;
+}
+
+.fade-to-dark.dark {
+ --taurus-placeholder-color: #b3b3b3;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+.content-container.switch-btn-bottom .nav-content {
+ margin-bottom: 1.5rem;
+}
+
+/* combo box */
+.clr-popover-content {
+ margin-top: 10px;
+}
+
+/* vmw search component */
+vdk-search .search-container cds-icon[shape=search] {
+ top: 2px !important;
+}
+
+/* forms */
+.destination-modal-body .custom-header-item .custom-header-input {
+ display: inline-block;
+ max-width: 10rem;
+}
+
+.modal-submit {
+ position: absolute;
+ left: -9999px;
+ z-index: -9999;
+ visibility: hidden;
+}
+
+vdk-form-section-container.p-header-section .form-header {
+ padding-bottom: 0 !important;
+ margin-top: 0;
+}
+
+vdk-form-section-container.p-header-section .form-header .section-title {
+ font-size: .7rem;
+ font-weight: 500;
+}
+
+vdk-form-section-container.p-header-section .vdk-vertical-line {
+ margin-top: 0 !important;
+}
+
+vdk-form-section-container.p-header-section .csp-edit-button {
+ margin-left: 1rem !important;
+}
+
+/* links */
+a.disabled {
+ pointer-events: none;
+ cursor: default;
+ opacity: 0.5;
+}
+
+/* theme */
+.toogle-theme {
+ position: absolute;
+ bottom: 0;
+ background-color: transparent !important;
+ padding-left: 0 !important;
+}
+
+.toogle-theme:hover {
+ background-color: transparent !important;
+}
+
+.toogle-theme .nav-link {
+ background-color: transparent !important;
+}
+
+.page-title {
+ margin-top: 0;
+}
+
+.tc {
+ text-align: center !important;
+}
+
+/* margins */
+.m-0 {
+ margin: 0 !important;
+}
+
+.mt-0 {
+ margin-top: 0 !important;
+}
+
+.mb-12 {
+ margin-bottom: 12px !important;
+}
+
+.pl-35 {
+ padding-left: 35px !important;
+}
+
+.w-100 {
+ width: 100% !important;
+}
+
+/* modals */
+.modal-body > :first-child {
+ margin-top: 0;
+}
+
+input.modal-form-control {
+ width: 12rem;
+}
+
+select.modal-form-control {
+ width: 8rem;
+}
+
+.modal-actions {
+ margin-top: -20px !important;
+}
+
+/* color styles */
+.red {
+ color: #EB5757;
+}
+
+/* position */
+.right {
+ float: right;
+}
+
+.left {
+ float: left;
+}
+
+/* lists */
+ul.idented {
+ text-indent: 2em;
+}
+
+/* media */
+@media (max-width: 600px) {
+ .right {
+ float: none !important;
+ }
+}
+
+/* datagrid cells */
+.cell-img-container {
+ min-width: 30px !important;
+ width: 60px !important;
+ text-align: center !important;
+}
+
+.cell-center {
+ margin: auto !important;
+}
+
+.enabled-column {
+ width: 6rem !important;
+}
+
+/* limit-height */
+.limit-height {
+ height: 50vh;
+}
+
+.limit-height clr-datagrid {
+ height: 50vh !important;
+}
+
+.limit-height clr-datagrid .datagrid {
+ margin-top: 0;
+}
+
+.limit-height clr-datagrid clr-dg-cell {
+ margin: auto;
+}
+
+/* team-list-component */
+.team-list-container .clr-form-control {
+ margin-top: 0 !important;
+}
+
+/* CSP-HEADER */
+/* Hide header assets */
+.header .header-actions .nav-icon.app-switcher {
+ width: 0;
+}
+
+.header .header-actions .nav-icon.app-switcher cds-icon {
+ display: none !important;
+}
+
+.header .header-actions .nav-icon.user-menu span.org-name {
+ display: none;
+}
+
+@media (min-width: 768px) {
+ .header .header-actions .nav-icon.user-menu div {
+ margin-top: .5rem;
+ }
+}
+
+.clr-vertical-nav .nav-group-trigger .nav-group-trigger-icon {
+ height: 0.8rem;
+}
+
+.page-wrapper {
+ padding-bottom: 2.4rem;
+}
+
+pre {
+ padding: 0.5rem;
+ overflow: auto;
+}
+
+p code {
+ color: #c21d00;
+}
+
+.table-documentation {
+ caption {
+ display: none;
+ }
+}
+
+.token {
+ &.attr-name, &.builtin, &.char &.inserted &.selector &.string {
+ color: #aa5d00;
+ }
+
+ &.atrule, &.attr-value, &.function {
+ color: #aa5d00;
+ }
+
+ &.constant, &.deleted, &.property, &.symbol, &.tag {
+ color: #007faa;
+ }
+
+ &.punctuation {
+ color: #545454;
+ }
+
+ &.class {
+ color: #306B00;
+ }
+
+ &.access {
+ color: #00567A;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/src/test.ts b/projects/frontend/shared-components/gui/projects/documentation-ui/src/test.ts
new file mode 100644
index 0000000000..d57e73ade7
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/src/test.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
+
+declare const require: {
+ context(path: string, deep?: boolean, filter?: RegExp): {
+ keys(): string[];
+ (id: string): T;
+ };
+};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting(),
+ {
+ teardown: { destroyAfterEach: false }
+ }
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.app.json b/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.app.json
new file mode 100644
index 0000000000..809c09ca71
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/app",
+ "types": []
+ },
+ "files": [
+ "src/main.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.spec.json b/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.spec.json
new file mode 100644
index 0000000000..aab257ed7d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/documentation-ui/tsconfig.spec.json
@@ -0,0 +1,17 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/spec",
+ "types": [
+ "jasmine"
+ ]
+ },
+ "files": [
+ "src/test.ts",
+ "src/polyfills.ts"
+ ],
+ "include": [
+ "src/**/*.spec.ts",
+ "src/**/*.d.ts"
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/.eslintrc.json b/projects/frontend/shared-components/gui/projects/shared/.eslintrc.json
new file mode 100644
index 0000000000..50742c8cfe
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/.eslintrc.json
@@ -0,0 +1,112 @@
+{
+ "extends": "../../.eslintrc.json",
+ "ignorePatterns": [
+ "!**/*"
+ ],
+ "overrides": [
+ {
+ "files": [
+ "*.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "projects/shared/tsconfig.lib.json"
+ ],
+ "createDefaultProgram": true
+ },
+ "rules": {
+ "@angular-eslint/component-selector": [
+ "error",
+ {
+ "type": "element",
+ "prefix": "shared",
+ "style": "kebab-case"
+ }
+ ],
+ "@angular-eslint/directive-selector": [
+ "error",
+ {
+ "type": "attribute",
+ "prefix": "shared",
+ "style": "camelCase"
+ }
+ ],
+ "@typescript-eslint/naming-convention": [
+ "warn",
+ {
+ "selector": "property",
+ "format": [
+ "camelCase"
+ ],
+ "leadingUnderscore": "allow",
+ "filter": {
+ "regex": "^((\\$\\..+))$",
+ "match": false
+ }
+ },
+ {
+ "selector": "classProperty",
+ "modifiers": [
+ "static"
+ ],
+ "format": [
+ "camelCase",
+ "UPPER_CASE"
+ ],
+ "leadingUnderscore": "allow"
+ }
+ ],
+ "@typescript-eslint/no-shadow": "error",
+ "no-shadow": "off"
+ }
+ },
+ {
+ "files": [
+ "*.spec.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "projects/shared/tsconfig.spec.json"
+ ]
+ },
+ "rules": {
+ "@typescript-eslint/naming-convention": [
+ "warn",
+ {
+ "selector": "objectLiteralProperty",
+ "format": [
+ "camelCase"
+ ],
+ "leadingUnderscore": "allow",
+ "filter": {
+ "regex": "^((\\$\\..+)|(test_))",
+ "match": false
+ }
+ },
+ {
+ "selector": "classProperty",
+ "modifiers": [
+ "static"
+ ],
+ "format": [
+ "camelCase",
+ "UPPER_CASE"
+ ],
+ "leadingUnderscore": "allow"
+ }
+ ],
+ "@typescript-eslint/ban-ts-comment": "off",
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-floating-promises": "off",
+ "@typescript-eslint/no-unsafe-assignment": "off",
+ "@typescript-eslint/unbound-method": "off"
+ }
+ },
+ {
+ "files": [
+ "*.html"
+ ],
+ "rules": {}
+ }
+ ]
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-card.svg b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-card.svg
new file mode 100644
index 0000000000..f3ee5a6061
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-card.svg
@@ -0,0 +1,198 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-popup.svg b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-popup.svg
new file mode 100644
index 0000000000..75dbebccf5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-popup.svg
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-steps.svg b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-steps.svg
new file mode 100644
index 0000000000..079a7b70c1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/animations/botour-steps.svg
@@ -0,0 +1,178 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-background.png b/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-background.png
new file mode 100644
index 0000000000..25d990ab13
Binary files /dev/null and b/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-background.png differ
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-success.svg b/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-success.svg
new file mode 100644
index 0000000000..059a5ed6fd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/images/botour-success.svg
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ success_01
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/images/coming-soon-plan.svg b/projects/frontend/shared-components/gui/projects/shared/assets/images/coming-soon-plan.svg
new file mode 100644
index 0000000000..bc2d12480b
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/images/coming-soon-plan.svg
@@ -0,0 +1,116 @@
+
+
+
+
+
+
+
+
+ 1-plan
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/images/desktop-B001.svg b/projects/frontend/shared-components/gui/projects/shared/assets/images/desktop-B001.svg
new file mode 100644
index 0000000000..891bdf7232
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/images/desktop-B001.svg
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+ desktop-B001
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/assets/images/robot-magnifying-glass.svg b/projects/frontend/shared-components/gui/projects/shared/assets/images/robot-magnifying-glass.svg
new file mode 100644
index 0000000000..eba817c461
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/assets/images/robot-magnifying-glass.svg
@@ -0,0 +1,186 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ robot magnifying glass
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/karma.conf.js b/projects/frontend/shared-components/gui/projects/shared/karma.conf.js
new file mode 100644
index 0000000000..a1d9c95ef0
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/karma.conf.js
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/1.0/config/configuration-file.html
+
+module.exports = function(config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular-devkit/build-angular'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-junit-reporter'),
+ require('karma-coverage'),
+ require('@angular-devkit/build-angular/plugins/karma')
+ ],
+ client: {
+ jasmine: {
+ // you can add configuration options for Jasmine here
+ // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
+ // for example, you can disable the random execution with `random: false`
+ // or set a specific seed with `seed: 4321`
+ },
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ jasmineHtmlReporter: {
+ suppressAll: true // removes the duplicated traces
+ },
+ coverageReporter: {
+ dir: require('path').join(__dirname, '../../reports/coverage/shared'),
+ subdir: '.',
+ reporters: [
+ //Code coverage - output in HTML file and Console(to be parsed in the CI/CD badge)
+ { type: 'html' },
+ { type: 'text-summary' },
+ { type: 'lcovonly' }
+ ],
+ check: {
+ global: {
+ lines: 80
+ }
+ }
+ },
+ reporters: ['progress', 'junit', 'coverage'],
+ junitReporter: {
+ outputDir: require('path').join(__dirname, '../../reports/test-results/shared'),
+ outputFile: 'unit-tests.xml',
+ useBrowserName: false
+ },
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['ChromeHeadless'],
+ customLaunchers: {
+ ChromeHeadless_No_Sandbox: {
+ base: 'ChromeHeadless',
+ flags: ['--no-sandbox']
+ }
+ },
+ singleRun: false,
+ restartOnFileChange: true
+ });
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/ng-package.json b/projects/frontend/shared-components/gui/projects/shared/ng-package.json
new file mode 100644
index 0000000000..62b983c8bb
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/ng-package.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "../../node_modules/ng-packagr/ng-package.schema.json",
+ "dest": "../../dist/shared",
+ "assets": [
+ "./assets"
+ ],
+ "lib": {
+ "entryFile": "src/public-api.ts"
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/package.json b/projects/frontend/shared-components/gui/projects/shared/package.json
new file mode 100644
index 0000000000..9698b9cfb4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/package.json
@@ -0,0 +1,51 @@
+{
+ "name": "@vdk/shared",
+ "version": "0.0.1",
+ "engines": {
+ "node": "^14.15.0 || >=16.10.0",
+ "npm": ">=8.5.5"
+ },
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "@angular/animations": "~13.3.10",
+ "@angular/cdk": "~13.3.8",
+ "@angular/common": "~13.3.10",
+ "@angular/compiler": "~13.3.10",
+ "@angular/core": "~13.3.10",
+ "@angular/forms": "~13.3.10",
+ "@angular/platform-browser": "~13.3.10",
+ "@angular/platform-browser-dynamic": "~13.3.10",
+ "@angular/router": "~13.3.10",
+ "@cds/core": "~5.7.2",
+ "@clr/angular": "~13.3.1",
+ "@clr/icons": "~13.0.2",
+ "@clr/ui": "~13.3.1",
+ "@ngrx/effects": "~13.2.0",
+ "@ngrx/router-store": "~13.2.0",
+ "@ngrx/store": "~13.2.0",
+ "@swimlane/ngx-graph": "~8.0.0",
+ "@webcomponents/custom-elements": "~1.5.0",
+ "angular-ng-autocomplete": "~2.0.8",
+ "d3": "~5.16.0",
+ "dompurify": "~2.3.8",
+ "lodash": "~4.17.21",
+ "ngx-clipboard": "~15.1.0",
+ "ngx-cookie-service": "~13.2.1",
+ "rxjs": "~7.5.5",
+ "zone.js": "~0.11.7"
+ },
+ "dependencies": {
+ "tslib": "2.3.1"
+ },
+ "description": "Taurus Shared library enables reusability of shared features like: NgRx Redux, Error Handlers, Utils, Generic Components etc.",
+ "repository": {
+ "type": "git",
+ "url": "-"
+ },
+ "keywords": [
+ "vdk",
+ "shared",
+ "versatile-data-kit"
+ ],
+ "author": "Versatile Data Kit (https://github.com/vmware/versatile-data-kit)"
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/index.ts
new file mode 100644
index 0000000000..412a9d8bd1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './request';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/index.ts
new file mode 100644
index 0000000000..d37bcae73a
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './request.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.spec.ts
new file mode 100644
index 0000000000..c3e677beec
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.spec.ts
@@ -0,0 +1,376 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ApiPredicate, RequestFilterImpl, RequestOrderImpl, RequestPageImpl } from './request.model';
+
+describe('RequestPage', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RequestPageImpl(0, 0);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(RequestPageImpl);
+ });
+
+ it('should verify correct value are assigned', () => {
+ // Given
+ const pageNumber = 4;
+ const pageSize = 10;
+
+ // When
+ const instance = new RequestPageImpl(pageNumber, pageSize);
+
+ // Then
+ expect(instance.page).toEqual(pageNumber);
+ expect(instance.size).toEqual(pageSize);
+ });
+
+ it('should verify on Nil parameters default value will be assigned', () => {
+ // When
+ const instance = new RequestPageImpl(null, undefined);
+
+ // Then
+ expect(instance.page).toEqual(1);
+ expect(instance.size).toEqual(25);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RequestPageImpl.of(8, 20);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestPageImpl);
+ expect(instance.page).toEqual(8);
+ expect(instance.size).toEqual(20);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with default values', () => {
+ // When
+ const instance = RequestPageImpl.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestPageImpl);
+ expect(instance.page).toEqual(1);
+ expect(instance.size).toEqual(25);
+ });
+ });
+
+ describe('|fromLiteral|', () => {
+ it('should verify will create new instance from given literal object', () => {
+ // Given
+ const literal = { pageNumber: 7, pageSize: 52 };
+
+ // When
+ const instance = RequestPageImpl.fromLiteral(literal);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestPageImpl);
+ expect(instance.page).toEqual(7);
+ expect(instance.size).toEqual(52);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|toLiteral|', () => {
+ it('should verify will create literal object from RequestPage object ', () => {
+ // Given
+ const requestPage = RequestPageImpl.of(5, 17);
+
+ // When
+ const literal = requestPage.toLiteral();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestPageImpl);
+ expect(literal).toBeInstanceOf(Object);
+ expect(literal.pageNumber).toEqual(5);
+ expect(literal.pageSize).toEqual(17);
+ });
+ });
+
+ describe('|toLiteralDeepClone|', () => {
+ it('should verify will create literal deep cloned object from RequestPage object ', () => {
+ // Given
+ const requestPage = RequestPageImpl.of(9, 13);
+
+ // When
+ const literal = requestPage.toLiteralDeepClone();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestPageImpl);
+ expect(literal).toBeInstanceOf(Object);
+ expect(literal.pageNumber).toEqual(9);
+ expect(literal.pageSize).toEqual(13);
+ });
+ });
+ });
+});
+
+describe('RequestOrder', () => {
+ let apiPredicate1: ApiPredicate;
+ let apiPredicate2: ApiPredicate;
+ let apiPredicate3: ApiPredicate;
+
+ beforeEach(() => {
+ apiPredicate1 = { pattern: 'test*', property: 'config.team', sort: 'DESC' };
+ apiPredicate2 = { pattern: 'mock*', property: 'config.job', sort: 'ASC' };
+ apiPredicate3 = { pattern: 'prod*', property: 'config.status', sort: 'DESC' };
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RequestOrderImpl();
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(RequestOrderImpl);
+ });
+
+ it('should verify correct value are assigned', () => {
+ // When
+ const instance = new RequestOrderImpl(apiPredicate1, apiPredicate2, apiPredicate3);
+
+ // Then
+ expect(instance.criteria).toBeInstanceOf(Array);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+
+ it('should verify wont assign Nil parameters', () => {
+ // When
+ const instance = new RequestOrderImpl(null, apiPredicate2, undefined, apiPredicate1, null, apiPredicate3, undefined);
+
+ // Then
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate2);
+ expect(instance.criteria[1]).toBe(apiPredicate1);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RequestOrderImpl.of(apiPredicate1, null, apiPredicate2, apiPredicate3);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestOrderImpl);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with no criteria', () => {
+ // When
+ const instance = RequestOrderImpl.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestOrderImpl);
+ expect(instance.criteria).toEqual([]);
+ });
+ });
+
+ describe('|fromLiteral|', () => {
+ it('should verify will create new instance from given literal Array of ApiPredicates', () => {
+ // Given
+ const literal: ApiPredicate[] = [apiPredicate1, apiPredicate2, apiPredicate3];
+
+ // When
+ const instance = RequestOrderImpl.fromLiteral(literal);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestOrderImpl);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|toLiteral|', () => {
+ it('should verify will create literal from RequestOrder object ', () => {
+ // Given
+ const requestOrder = RequestOrderImpl.of(apiPredicate1, null, apiPredicate2, undefined, apiPredicate3);
+
+ // When
+ const literal = requestOrder.toLiteral();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestOrderImpl);
+ expect(literal).toBeInstanceOf(Array);
+ expect(literal[0]).toBe(apiPredicate1);
+ expect(literal[1]).toBe(apiPredicate2);
+ expect(literal[2]).toBe(apiPredicate3);
+ });
+ });
+
+ describe('|toLiteralDeepClone|', () => {
+ it('should verify will create literal deep cloned from RequestPage object ', () => {
+ // Given
+ const requestOrder = RequestOrderImpl.of(apiPredicate3, null, apiPredicate1, undefined, apiPredicate2);
+
+ // When
+ const literal = requestOrder.toLiteralDeepClone();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestOrderImpl);
+ expect(literal).toBeInstanceOf(Array);
+ expect(literal[0]).not.toBe(apiPredicate3);
+ expect(literal[1]).not.toBe(apiPredicate1);
+ expect(literal[2]).not.toBe(apiPredicate2);
+ expect(literal[0]).toEqual(apiPredicate3);
+ expect(literal[1]).toEqual(apiPredicate1);
+ expect(literal[2]).toEqual(apiPredicate2);
+ });
+ });
+ });
+});
+
+describe('RequestFilter', () => {
+ let apiPredicate1: ApiPredicate;
+ let apiPredicate2: ApiPredicate;
+ let apiPredicate3: ApiPredicate;
+
+ beforeEach(() => {
+ apiPredicate1 = { pattern: 'test*', property: 'config.team', sort: 'DESC' };
+ apiPredicate2 = { pattern: 'mock*', property: 'config.job', sort: 'ASC' };
+ apiPredicate3 = { pattern: 'prod*', property: 'config.status', sort: 'DESC' };
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RequestFilterImpl();
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(RequestFilterImpl);
+ });
+
+ it('should verify correct value are assigned', () => {
+ // When
+ const instance = new RequestFilterImpl(apiPredicate1, apiPredicate2, apiPredicate3);
+
+ // Then
+ expect(instance.criteria).toBeInstanceOf(Array);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+
+ it('should verify wont assign Nil parameters', () => {
+ // When
+ const instance = new RequestFilterImpl(null, apiPredicate2, undefined, apiPredicate1, null, apiPredicate3, undefined);
+
+ // Then
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate2);
+ expect(instance.criteria[1]).toBe(apiPredicate1);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RequestFilterImpl.of(apiPredicate1, null, apiPredicate2, apiPredicate3);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestFilterImpl);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with no criteria', () => {
+ // When
+ const instance = RequestFilterImpl.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestFilterImpl);
+ expect(instance.criteria).toEqual([]);
+ });
+ });
+
+ describe('|fromLiteral|', () => {
+ it('should verify will create new instance from given literal Array of ApiPredicates', () => {
+ // Given
+ const literal: ApiPredicate[] = [apiPredicate1, apiPredicate2, apiPredicate3];
+
+ // When
+ const instance = RequestFilterImpl.fromLiteral(literal);
+
+ // Then
+ expect(instance).toBeInstanceOf(RequestFilterImpl);
+ expect(instance.criteria.length).toEqual(3);
+ expect(instance.criteria[0]).toBe(apiPredicate1);
+ expect(instance.criteria[1]).toBe(apiPredicate2);
+ expect(instance.criteria[2]).toBe(apiPredicate3);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|toLiteral|', () => {
+ it('should verify will create literal from RequestFilter object ', () => {
+ // Given
+ const requestFilter = RequestFilterImpl.of(apiPredicate1, null, apiPredicate2, undefined, apiPredicate3);
+
+ // When
+ const literal = requestFilter.toLiteral();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestFilterImpl);
+ expect(literal).toBeInstanceOf(Array);
+ expect(literal[0]).toBe(apiPredicate1);
+ expect(literal[1]).toBe(apiPredicate2);
+ expect(literal[2]).toBe(apiPredicate3);
+ });
+ });
+
+ describe('|toLiteralDeepClone|', () => {
+ it('should verify will create literal deep cloned from RequestFilter object ', () => {
+ // Given
+ const requestFilter = RequestFilterImpl.of(apiPredicate3, null, apiPredicate1, undefined, apiPredicate2);
+
+ // When
+ const literal = requestFilter.toLiteralDeepClone();
+
+ // Then
+ expect(literal).not.toBeInstanceOf(RequestFilterImpl);
+ expect(literal).toBeInstanceOf(Array);
+ expect(literal[0]).not.toBe(apiPredicate3);
+ expect(literal[1]).not.toBe(apiPredicate1);
+ expect(literal[2]).not.toBe(apiPredicate2);
+ expect(literal[0]).toEqual(apiPredicate3);
+ expect(literal[1]).toEqual(apiPredicate1);
+ expect(literal[2]).toEqual(apiPredicate2);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.ts
new file mode 100644
index 0000000000..219ec59109
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/http/request/request.model.ts
@@ -0,0 +1,197 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Literal } from '../../interfaces';
+import { CollectionsUtil } from '../../../utils';
+
+// Page DTO
+
+export type LiteralRequestPage = { pageNumber: number; pageSize: number };
+
+export interface RequestPage extends Literal {
+ readonly page: number;
+ readonly size: number;
+}
+
+/**
+ * ** Request Page DTO.
+ *
+ *
+ */
+export class RequestPageImpl implements RequestPage {
+ public readonly page: number;
+ public readonly size: number;
+
+ constructor(page: number, size: number) {
+ this.page = page ?? 1;
+ this.size = size ?? 25;
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(page: number, size: number): RequestPageImpl {
+ return new RequestPageImpl(page, size);
+ }
+
+ /**
+ * ** Factory method for empty RequestPageDTO.
+ */
+ static empty(): RequestPageImpl {
+ return new RequestPageImpl(null, null);
+ }
+
+ /**
+ * ** Creates DTO from literal.
+ */
+ static fromLiteral(literalDTO: { pageNumber: number; pageSize: number }): RequestPageImpl {
+ return RequestPageImpl.of(literalDTO.pageNumber, literalDTO.pageSize);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteral(): LiteralRequestPage {
+ return {
+ pageNumber: this.page ?? 1,
+ pageSize: this.size ?? 25
+ };
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteralDeepClone(): LiteralRequestPage {
+ return this.toLiteral();
+ }
+}
+
+// Order DTO
+
+export interface RequestOrder extends Literal {
+ readonly criteria: ApiPredicate[];
+}
+
+/**
+ * ** Request Order DTO.
+ *
+ *
+ */
+export class RequestOrderImpl implements RequestOrder {
+ public readonly criteria: ApiPredicate[];
+
+ constructor(...criteria: ApiPredicate[]) {
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ this.criteria = [...criteria.filter(CollectionsUtil.isDefined)];
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(...criteria: ApiPredicate[]): RequestOrderImpl {
+ return new RequestOrderImpl(...criteria);
+ }
+
+ /**
+ * ** Factory method for empty RequestOrderDTO.
+ */
+ static empty(): RequestOrderImpl {
+ return new RequestOrderImpl();
+ }
+
+ /**
+ * ** Creates DTO from literal.
+ */
+ static fromLiteral(literalCriteria: Array): RequestOrderImpl {
+ return RequestOrderImpl.of(...literalCriteria);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteral(): LiteralApiPredicates {
+ return [...this.criteria];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteralDeepClone(): LiteralApiPredicates {
+ return this.criteria
+ .map((c) => ({ ...c }));
+ }
+}
+
+// Filter DTO
+
+export interface RequestFilter extends Literal {
+ readonly criteria: ApiPredicate[];
+}
+
+/**
+ * ** Request Filter DTO.
+ *
+ *
+ */
+export class RequestFilterImpl implements RequestFilter {
+ public readonly criteria: ApiPredicate[];
+
+ constructor(...criteria: ApiPredicate[]) {
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ this.criteria = [...criteria.filter(CollectionsUtil.isDefined)];
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(...criteria: ApiPredicate[]): RequestFilterImpl {
+ return new RequestFilterImpl(...criteria);
+ }
+
+ /**
+ * ** Factory method for empty RequestFilterDTO.
+ */
+ static empty(): RequestFilterImpl {
+ return new RequestFilterImpl();
+ }
+
+ /**
+ * ** Creates DTO from literal.
+ */
+ static fromLiteral(literalCriteria: Array): RequestFilterImpl {
+ return RequestFilterImpl.of(...literalCriteria);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteral(): LiteralApiPredicates {
+ return [...this.criteria];
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteralDeepClone(): LiteralApiPredicates {
+ return this.criteria
+ .map((c) => ({ ...c }));
+ }
+}
+
+// Generic Predicate for API
+
+export type LiteralApiPredicates = Array;
+
+export const ASC = 'ASC';
+export const DESC = 'DESC';
+export type DirectionType = typeof ASC | typeof DESC;
+
+export interface ApiPredicate {
+ readonly property: string;
+ readonly pattern: string;
+ readonly sort: DirectionType;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/index.ts
new file mode 100644
index 0000000000..8698258268
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/index.ts
@@ -0,0 +1,13 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './http';
+export * from './interfaces';
+export * from './object';
+export * from './predicate';
+export * from './route';
+export * from './tasks';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/comparable.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/comparable.interface.ts
new file mode 100644
index 0000000000..147d9d9e97
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/comparable.interface.ts
@@ -0,0 +1,72 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * ** Interface for Comparison data.
+ *
+ *
+ */
+export interface Comparable {
+ /**
+ * ** Value stored in Comparable for Comparison.
+ */
+ readonly value: T;
+
+ /**
+ * ** Compares stored data with the provided one.
+ *
+ * -1 if the stored value is less than the provided value.
+ * 0 if both values are equal.
+ * 1 if stored value is bigger than the provided value.
+ */
+ compare(comparable: Comparable): number;
+
+ /**
+ * ** Verify if stored value is null or undefined and returns true.
+ */
+ isNil(): boolean;
+
+ /**
+ * ** Verify if stored value is not null and not undefined.
+ */
+ notNil(): boolean;
+
+ /**
+ * ** Compare if stored value is similar to provided value.
+ */
+ like(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is equals with provided value.
+ */
+ equal(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is different than provided value.
+ */
+ notEqual(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is less than provided value.
+ */
+ lessThan(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is less or equal than provided value.
+ */
+ lessThanInclusive(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is greater than provided value.
+ */
+ greaterThan(comparable: Comparable): boolean;
+
+ /**
+ * ** Verify if stored value is greater or equal than provided value.
+ */
+ greaterThanInclusive(comparable: Comparable): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/copy.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/copy.interface.ts
new file mode 100644
index 0000000000..ad29bc74cc
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/copy.interface.ts
@@ -0,0 +1,21 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+/**
+ * ** Interface for Copy of Object.
+ *
+ *
+ */
+export interface Copy> {
+ /**
+ * ** Make shallow copy of current Object.
+ * - Optionally provide partial Object to merge on top of the current one.
+ */
+ copy(partial?: Partial);
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/expression.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/expression.interface.ts
new file mode 100644
index 0000000000..492aca817c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/expression.interface.ts
@@ -0,0 +1,26 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable } from './comparable.interface';
+import { Predicate } from './predicate.interface';
+
+/**
+ * ** Interface for Expression.
+ *
+ *
+ */
+export interface Expression {
+ /**
+ * ** Predicates Array.
+ */
+ readonly predicates: T[];
+
+ /**
+ * ** Evaluate Expression to boolean (true or false).
+ */
+ evaluate(comparable?: Comparable): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/index.ts
new file mode 100644
index 0000000000..7ffff61d6d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/index.ts
@@ -0,0 +1,14 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './comparable.interface';
+export * from './copy.interface';
+export * from './expression.interface';
+export * from './literal.interface';
+export * from './predicate.interface';
+export * from './replacer.interface';
+export * from './serializable.interface';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/literal.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/literal.interface.ts
new file mode 100644
index 0000000000..d81fe5c61d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/literal.interface.ts
@@ -0,0 +1,30 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { PrimitivesNil, PrimitivesNilArrays, PrimitivesNilObject } from '../../utils';
+
+type SerializedType = PrimitivesNil | PrimitivesNilArrays | PrimitivesNilObject | unknown;
+
+/**
+ * ** This interface gives boundaries for Class instances to get converted into Literals.
+ *
+ *
+ */
+export interface Literal {
+ /**
+ * ** Implements this method and return data you want to be serialized into Literals.
+ */
+ toLiteral(): T;
+
+ /**
+ * ** Implements this method and return data you want to be serialized into Literals.
+ *
+ * - Data should be deep clone before return, to comply with immutability.
+ *
+ */
+ toLiteralDeepClone(): T;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/predicate.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/predicate.interface.ts
new file mode 100644
index 0000000000..8531fa0644
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/predicate.interface.ts
@@ -0,0 +1,25 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable } from './comparable.interface';
+
+/**
+ * ** Interface for Predicate Classes.
+ *
+ *
+ */
+export interface Predicate {
+ /**
+ * ** Stored comparable that have to be compared with provided comparable.
+ */
+ readonly comparable: T;
+
+ /**
+ * ** Evaluate Predicate to boolean (true or false).
+ */
+ evaluate(comparable: C): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/replacer.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/replacer.interface.ts
new file mode 100644
index 0000000000..462712e1c7
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/replacer.interface.ts
@@ -0,0 +1,16 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * ** Interface for generic replacer.
+ *
+ *
+ */
+export interface Replacer {
+ searchValue: T;
+ replaceValue: T;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/serializable.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/serializable.interface.ts
new file mode 100644
index 0000000000..452af94619
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/interfaces/serializable.interface.ts
@@ -0,0 +1,22 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { PrimitivesNil, PrimitivesNilArrays, PrimitivesNilObject } from '../../utils';
+
+type SerializedType = PrimitivesNil | PrimitivesNilArrays | PrimitivesNilObject | unknown;
+
+/**
+ * ** This interface gives boundaries for Objects that we want to be serializable for JSON.
+ *
+ *
+ */
+export interface Serializable {
+ /**
+ * ** Implements this method and return data you want to be serialized when JSON.stringify(...) is executed.
+ */
+ toJSON(): T;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/index.ts
new file mode 100644
index 0000000000..787b0abb40
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/index.ts
new file mode 100644
index 0000000000..dcc4045eb1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './taurus-object.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.spec.ts
new file mode 100644
index 0000000000..3824703266
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.spec.ts
@@ -0,0 +1,158 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation */
+
+import { Subscription } from 'rxjs';
+
+import { CallFake } from '../../../unit-testing';
+
+import { TaurusObject } from './taurus-object.model';
+
+describe('TaurusObject', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new TaurusObject();
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(TaurusObject);
+ });
+
+ describe('Properties::', () => {
+ describe('|subscriptions|', () => {
+ it('should verify default value is empty Array', () => {
+ // Given
+ const instance = new TaurusObject();
+
+ // Then
+ expect(instance['subscriptions']).toEqual([]);
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|dispose|', () => {
+ it('should verify will clean all subscriptions', () => {
+ // Given
+ const subscription1 = jasmine.createSpyObj('subscription1', ['unsubscribe']);
+ const subscription2 = jasmine.createSpyObj('subscription2', ['unsubscribe']);
+ const subscription3 = jasmine.createSpyObj('subscription3', ['unsubscribe']);
+ const instance = new TaurusObject();
+ instance['subscriptions'].push(subscription1, null, subscription2, undefined, subscription3);
+
+ // When
+ instance.dispose();
+
+ // Then
+ expect(subscription1.unsubscribe).toHaveBeenCalled();
+ expect(subscription2.unsubscribe).toHaveBeenCalled();
+ expect(subscription3.unsubscribe).toHaveBeenCalled();
+ });
+ });
+
+ describe('|removeSubscriptionRef|', () => {
+ let subscription1: Subscription;
+ let subscription2: Subscription;
+ let subscription3: Subscription;
+ let instance: TaurusObject;
+
+ beforeEach(() => {
+ subscription1 = new Subscription();
+ subscription2 = new Subscription();
+ subscription3 = new Subscription();
+ instance = new TaurusObject();
+ });
+
+ it('should verify will remove provided subscription from buffer', () => {
+ // Given
+ instance['subscriptions'].push(subscription1, null, undefined, subscription2, subscription3);
+ const unsubscribeSpy = spyOn(subscription2, 'unsubscribe').and.callThrough();
+
+ // Then 1
+ expect(instance['subscriptions'].length).toEqual(5);
+
+ // When
+ // @ts-ignore
+ const value = instance.removeSubscriptionRef(subscription2);
+
+ // Then 2
+ expect(value).toBeTrue();
+ expect(unsubscribeSpy).toHaveBeenCalled();
+ expect(instance['subscriptions'].length).toEqual(4);
+ });
+
+ it('should verify will remove provided subscription from buffer and unsubscribe error will be logged in console', () => {
+ // Given
+ instance['subscriptions'].push(subscription1);
+ const error = new Error('Error');
+ const unsubscribeSpy = spyOn(subscription1, 'unsubscribe').and.throwError(error);
+ const consoleErrorSpy = spyOn(console, 'error').and.callFake(CallFake);
+
+ // When
+ // @ts-ignore
+ const value = instance.removeSubscriptionRef(subscription1);
+
+ // Then 2
+ expect(value).toBeFalse();
+ expect(unsubscribeSpy).toHaveBeenCalled();
+ expect(consoleErrorSpy).toHaveBeenCalledWith(`Taurus Object failed to unsubscribe from rxjs stream!`, error);
+ });
+
+ it(`should verify will unsubscribe even reference doesn't exist in buffer`, () => {
+ // Given
+ instance['subscriptions'].push(subscription2, null, undefined);
+ const unsubscribeSpy = spyOn(subscription1, 'unsubscribe').and.callThrough();
+
+ // Then 1
+ expect(instance['subscriptions'].length).toEqual(3);
+
+ // When
+ // @ts-ignore
+ const value = instance.removeSubscriptionRef(subscription1);
+
+ // Then 2
+ expect(value).toBeTrue();
+ expect(unsubscribeSpy).toHaveBeenCalled();
+ expect(instance['subscriptions'].length).toEqual(3);
+ });
+
+ it(`should verify will return false when provided reference is not Subscription instance`, () => {
+ // Given
+ instance['subscriptions'].push(subscription1, subscription2);
+
+ // Then 1
+ expect(instance['subscriptions'].length).toEqual(2);
+
+ // When
+ // @ts-ignore
+ const value1 = instance.removeSubscriptionRef(null);
+ // @ts-ignore
+ const value2 = instance.removeSubscriptionRef(undefined);
+
+ // Then 2
+ expect(value1).toBeFalse();
+ expect(value2).toBeFalse();
+ expect(instance['subscriptions'].length).toEqual(2);
+ });
+ });
+
+ describe('|ngOnDestroy|', () => {
+ it('should verify will invoke correct method', () => {
+ // Given
+ const instance = new TaurusObject();
+ const disposeSpy = spyOn(instance, 'dispose').and.callFake(CallFake);
+
+ // When
+ instance.ngOnDestroy();
+
+ // Then
+ expect(disposeSpy).toHaveBeenCalled();
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.ts
new file mode 100644
index 0000000000..00ebef9bd5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/model/taurus-object.model.ts
@@ -0,0 +1,92 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Directive, OnDestroy } from '@angular/core';
+
+import { Subscription } from 'rxjs';
+import { CollectionsUtil } from '../../../utils';
+
+/**
+ * ** Base Class for all Angular related Objects.
+ *
+ * - Cleans all rxjs subscriptions on object destroy.
+ *
+ *
+ */
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export class TaurusObject implements OnDestroy {
+
+ /**
+ * ** Store for Subscriptions references.
+ */
+ protected subscriptions: Subscription[];
+
+ /**
+ * ** Constructor.
+ */
+ constructor() {
+ this.subscriptions = [];
+ }
+
+ /**
+ * ** Methods that will dispose Object.
+ * - Clean all Subscriptions.
+ */
+ dispose(): void {
+ this.cleanSubscriptions();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ ngOnDestroy() {
+ this.dispose();
+ }
+
+ /**
+ * ** Clean all Subscriptions.
+ */
+ protected cleanSubscriptions(): void {
+ // unsubscribe all valid subscriptions
+ this.subscriptions
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ .filter(CollectionsUtil.isDefined)
+ // eslint-disable-next-line @typescript-eslint/unbound-method
+ .forEach(TaurusObject._unsubscribeFromStream);
+ }
+
+ protected removeSubscriptionRef(subscriptionRef: Subscription): boolean {
+ const subscriptionIndex = this.subscriptions.findIndex((s) => s === subscriptionRef);
+
+ if (subscriptionIndex === -1) {
+ if (subscriptionRef instanceof Subscription) {
+ TaurusObject._unsubscribeFromStream(subscriptionRef);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ const removedSubscription = this.subscriptions.splice(subscriptionIndex, 1);
+
+ return TaurusObject._unsubscribeFromStream(removedSubscription[0]);
+ }
+
+ private static _unsubscribeFromStream(s: Subscription): boolean {
+ try {
+ s.unsubscribe();
+
+ return true;
+ } catch (e) {
+ console.error(`Taurus Object failed to unsubscribe from rxjs stream!`, e);
+
+ return false;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/object/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.spec.ts
new file mode 100644
index 0000000000..4ded4754d3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.spec.ts
@@ -0,0 +1,305 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable } from '../../interfaces';
+
+import { ComparableImpl } from './comparable.impl';
+
+class ComparableStub implements Comparable {
+ public readonly value: unknown;
+
+ constructor(value: unknown) {
+ this.value = value;
+ }
+
+ compare(_comparable: Comparable): number {
+ return 0;
+ }
+
+ equal(_comparable: Comparable): boolean {
+ return true;
+ }
+
+ like(_comparable: Comparable): boolean {
+ return true;
+ }
+
+ notEqual(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ isNil(): boolean {
+ return false;
+ }
+
+ notNil(): boolean {
+ return true;
+ }
+
+ greaterThan(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ greaterThanInclusive(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ lessThan(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ lessThanInclusive(_comparable: Comparable): boolean {
+ return false;
+ }
+}
+
+describe('ComparableImpl', () => {
+ it('should verify instance is created', () => {
+ // Given
+ const value = 'VDK';
+
+ // When
+ const instance = new ComparableImpl(value);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ it('should verify value is correctly assigned', () => {
+ // Given
+ const value = 'VDK';
+
+ // When
+ const instance = new ComparableImpl(value);
+
+ // Then
+ expect(instance.value).toBe(value);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::()', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // Given
+ const value = 'VDK';
+
+ // When
+ const instance = ComparableImpl.of(value);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(ComparableImpl);
+ });
+ });
+ });
+ });
+
+ describe('Methods::()', () => {
+ let v1: unknown;
+ let v2: unknown;
+ let v3: unknown;
+ let v4: unknown;
+ let v5: unknown;
+ let v6: unknown;
+ let v7: unknown;
+ let v8: unknown;
+
+ let c1: ComparableImpl;
+ let c2: ComparableImpl;
+ let c3: ComparableImpl;
+ let c4: ComparableImpl;
+ let c5: ComparableImpl;
+ let c6: ComparableImpl;
+ let c7: ComparableImpl;
+ let c8: ComparableImpl;
+
+ beforeEach(() => {
+ v1 = null;
+ v2 = undefined;
+ v3 = 'Saggitarius';
+ v4 = 10;
+ v5 = 'Saggitarius';
+ v6 = 11;
+ v7 = 10;
+ v8 = 'Taurus';
+
+ c1 = ComparableImpl.of(v1);
+ c2 = ComparableImpl.of(v2);
+ c3 = ComparableImpl.of(v3);
+ c4 = ComparableImpl.of(v4);
+ c5 = ComparableImpl.of(v5);
+ c6 = ComparableImpl.of(v6);
+ c7 = ComparableImpl.of(v7);
+ c8 = ComparableImpl.of(v8);
+ });
+
+ describe('|compare|', () => {
+ it('should verify will return 0 for equal', () => {
+ // When
+ const comparison = c3.compare(c5);
+
+ // Then
+ expect(comparison).toEqual(0);
+ });
+
+ it('should verify will return 1 for greaterThan', () => {
+ // When
+ const comparison = c8.compare(c5);
+
+ // Then
+ expect(comparison).toEqual(1);
+ });
+
+ it('should verify will return -1 for lessThan', () => {
+ // When
+ const comparison = c5.compare(c8);
+
+ // Then
+ expect(comparison).toEqual(-1);
+ });
+
+ it('should verify will return -1 given comparable is not instance of the current Constructor', () => {
+ // Given
+ const c10 = new ComparableStub(v2);
+
+ // When
+ const comparison = c5.compare(c10);
+
+ // Then
+ expect(comparison).toEqual(-1);
+ });
+ });
+
+ describe('|isNil|', () => {
+ it('should verify will return true if value is null or undefined, otherwise false', () => {
+ // When
+ const r1 = c1.isNil();
+ const r2 = c2.isNil();
+ const r3 = c3.isNil();
+ const r4 = c4.isNil();
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeTrue();
+ expect(r3).toBeFalse();
+ expect(r4).toBeFalse();
+ });
+ });
+
+ describe('|notNil|', () => {
+ it('should verify will return false if value is null or undefined, otherwise true', () => {
+ // When
+ const r1 = c1.notNil();
+ const r2 = c2.notNil();
+ const r3 = c3.notNil();
+ const r4 = c4.notNil();
+
+ // Then
+ expect(r1).toBeFalse();
+ expect(r2).toBeFalse();
+ expect(r3).toBeTrue();
+ expect(r4).toBeTrue();
+ });
+ });
+
+ describe('|like|', () => {
+ it('should verify will return true if values are similar, otherwise false', () => {
+ // When
+ const r1 = c3.like(c5);
+ const r2 = c3.like(c8);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ });
+ });
+
+ describe('|equal|', () => {
+ it('should verify will return true if values are equal, otherwise false', () => {
+ // When
+ const r1 = c3.equal(c5);
+ const r2 = c3.equal(c8);
+ const r3 = c4.equal(c7);
+ const r4 = c4.equal(c6);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ expect(r3).toBeTrue();
+ expect(r4).toBeFalse();
+ });
+ });
+
+ describe('|notEqual|', () => {
+ it('should verify will return true if values are not equal, otherwise false', () => {
+ // When
+ const r1 = c3.notEqual(c5);
+ const r2 = c3.notEqual(c8);
+ const r3 = c4.notEqual(c7);
+ const r4 = c4.notEqual(c6);
+
+ // Then
+ expect(r1).toBeFalse();
+ expect(r2).toBeTrue();
+ expect(r3).toBeFalse();
+ expect(r4).toBeTrue();
+ });
+ });
+
+ describe('|lessThan|', () => {
+ it('should verify will return true if value is less than provided, otherwise false', () => {
+ // When
+ const r1 = c4.lessThan(c6);
+ const r2 = c6.lessThan(c4);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ });
+ });
+
+ describe('|lessThanInclusive|', () => {
+ it('should verify will return true if value is less than or equal to provided, otherwise false', () => {
+ // When
+ const r1 = c4.lessThanInclusive(c6);
+ const r2 = c6.lessThanInclusive(c4);
+ const r3 = c4.lessThanInclusive(c7);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ expect(r3).toBeTrue();
+ });
+ });
+
+ describe('|greaterThan|', () => {
+ it('should verify will return true if value is greater than provided, otherwise false', () => {
+ // When
+ const r1 = c6.greaterThan(c4);
+ const r2 = c4.greaterThan(c6);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ });
+ });
+
+ describe('|greaterThanInclusive|', () => {
+ it('should verify will return true if value is greater than or equal to provided, otherwise false', () => {
+ // When
+ const r1 = c6.greaterThanInclusive(c4);
+ const r2 = c4.greaterThanInclusive(c6);
+ const r3 = c7.greaterThanInclusive(c4);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ expect(r3).toBeTrue();
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.ts
new file mode 100644
index 0000000000..e5a485f457
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/comparable.impl.ts
@@ -0,0 +1,119 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { Comparable } from '../../interfaces';
+
+import { CollectionsUtil } from '../../../utils';
+
+/**
+ * ** Comparable.
+ *
+ *
+ */
+export class ComparableImpl implements Comparable {
+
+ /**
+ * @inheritDoc
+ */
+ public readonly value: T;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(value: T) {
+ this.value = value;
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(value: any): ComparableImpl {
+ return new ComparableImpl(value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ compare(comparable: Comparable): number {
+ if (comparable instanceof ComparableImpl) {
+ const evaluateSecondStatement = () => this.value > comparable.value
+ ? 1
+ : -1;
+
+ return this.value === comparable.value
+ ? 0
+ : evaluateSecondStatement();
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ isNil(): boolean {
+ return CollectionsUtil.isNil(this.value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ notNil(): boolean {
+ return CollectionsUtil.isDefined(this.value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ like(comparable: Comparable): boolean {
+ return this.compare(comparable) === 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ equal(comparable: Comparable): boolean {
+ return this.compare(comparable) === 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ notEqual(comparable: Comparable): boolean {
+ return this.compare(comparable) !== 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ lessThan(comparable: Comparable): boolean {
+ return this.compare(comparable) < 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ lessThanInclusive(comparable: Comparable): boolean {
+ return this.compare(comparable) <= 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ greaterThan(comparable: Comparable): boolean {
+ return this.compare(comparable) > 0;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ greaterThanInclusive(comparable: Comparable): boolean {
+ return this.compare(comparable) >= 0;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/index.ts
new file mode 100644
index 0000000000..785b6e3731
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './comparable.impl';
+export * from './predicates-comparable.impl';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.spec.ts
new file mode 100644
index 0000000000..d6647a0008
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.spec.ts
@@ -0,0 +1,320 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CallFake } from '../../../unit-testing';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from './predicates-comparable.impl';
+
+class ComparableStub implements Comparable {
+ public readonly value: any;
+
+ constructor(value: any) {
+ this.value = value;
+ }
+
+ compare(_comparable: Comparable): number {
+ return 0;
+ }
+
+ equal(_comparable: Comparable): boolean {
+ return true;
+ }
+
+ like(_comparable: Comparable): boolean {
+ return true;
+ }
+
+ notEqual(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ isNil(): boolean {
+ return false;
+ }
+
+ notNil(): boolean {
+ return true;
+ }
+
+ greaterThan(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ greaterThanInclusive(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ lessThan(_comparable: Comparable): boolean {
+ return false;
+ }
+
+ lessThanInclusive(_comparable: Comparable): boolean {
+ return false;
+ }
+}
+
+class PredicateEqualStub implements Predicate {
+ comparable: ComparableStub;
+
+ constructor(comparable: ComparableStub) {
+ this.comparable = comparable;
+ }
+
+ evaluate(comparable: Comparable): boolean {
+ return this.comparable.equal(comparable);
+ }
+}
+
+describe('PredicatesComparable', () => {
+ let predicate1: Predicate;
+ let predicate2: Predicate;
+ let predicate3: Predicate;
+
+ beforeEach(() => {
+ predicate1 = new PredicateEqualStub(new ComparableStub('A'));
+ predicate2 = new PredicateEqualStub(new ComparableStub('B'));
+ predicate3 = new PredicateEqualStub(new ComparableStub('1'));
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new PredicatesComparable(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ it('should verify value is correctly assigned', () => {
+ // When
+ const instance = new PredicatesComparable(predicate1, predicate2);
+
+ // Then
+ expect(instance.value[0]).toBe(predicate1);
+ expect(instance.value[1]).toBe(predicate2);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::()', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = PredicatesComparable.of(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(PredicatesComparable);
+ expect(instance.value[0]).toBe(predicate1);
+ expect(instance.value[1]).toBe(predicate2);
+ });
+ });
+ });
+ });
+
+ describe('Methods::()', () => {
+ let predicatesComparable: PredicatesComparable;
+ let injectedComparable: ComparableStub;
+ let consoleWarnSpy: jasmine.Spy;
+
+ beforeEach(() => {
+ predicatesComparable = PredicatesComparable.of(predicate1, predicate2);
+ injectedComparable = new ComparableStub('C');
+ consoleWarnSpy = spyOn(console, 'warn').and.callFake(CallFake);
+ });
+
+ describe('|compare|', () => {
+ it('should verify will return -1 and log warn to console', () => {
+ // When
+ const comparison = predicatesComparable.compare(injectedComparable);
+
+ // Then
+ expect(comparison).toEqual(-1);
+ expect(consoleWarnSpy).toHaveBeenCalledWith('PredicatesComparable, unsupported comparison.');
+ });
+ });
+
+ describe('|isNil|', () => {
+ it('should verify will return false all the time', () => {
+ // Given
+ const c1 = PredicatesComparable.of(null);
+ const c2 = PredicatesComparable.of(undefined);
+ const c3 = PredicatesComparable.of(predicate1);
+
+ // When
+ const r1 = c1.isNil();
+ const r2 = c2.isNil();
+ const r3 = c3.isNil();
+
+ // Then
+ expect(r1).toBeFalse();
+ expect(r2).toBeFalse();
+ expect(r3).toBeFalse();
+ });
+ });
+
+ describe('|notNil|', () => {
+ it('should verify will return true all the time', () => {
+ // Given
+ const c1 = PredicatesComparable.of(null);
+ const c2 = PredicatesComparable.of(undefined);
+ const c3 = PredicatesComparable.of(predicate1);
+
+ // When
+ const r1 = c1.notNil();
+ const r2 = c2.notNil();
+ const r3 = c3.notNil();
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeTrue();
+ expect(r3).toBeTrue();
+ });
+ });
+
+ describe('|like|', () => {
+ it('should verify will return true if one or more predicates return true, otherwise false', () => {
+ // Given
+ const predicate1EvaluateSpy = spyOn(predicate1, 'evaluate').and.returnValues(false, false);
+ const predicate2EvaluateSpy = spyOn(predicate2, 'evaluate').and.returnValues(true, false);
+ const predicate3EvaluateSpy = spyOn(predicate3, 'evaluate').and.returnValues(false);
+ const comparableStub = new ComparableStub('D');
+ predicatesComparable = PredicatesComparable.of(predicate1, predicate2, predicate3);
+
+ // When
+ const r1 = predicatesComparable.like(injectedComparable);
+ const r2 = predicatesComparable.like(comparableStub);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(predicate1EvaluateSpy.calls.argsFor(0)).toEqual([injectedComparable]);
+ expect(predicate2EvaluateSpy.calls.argsFor(0)).toEqual([injectedComparable]);
+
+ expect(r2).toBeFalse();
+ expect(predicate1EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub]);
+ expect(predicate2EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub]);
+ expect(predicate3EvaluateSpy.calls.argsFor(0)).toEqual([comparableStub]);
+
+ expect(predicate1EvaluateSpy).toHaveBeenCalledTimes(2);
+ expect(predicate2EvaluateSpy).toHaveBeenCalledTimes(2);
+ expect(predicate3EvaluateSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('|equal|', () => {
+ it('should verify will return true if all predicates return true, otherwise false', () => {
+ // Given
+ const predicate1EvaluateSpy = spyOn(predicate1, 'evaluate').and.returnValues(true, false, true);
+ const predicate2EvaluateSpy = spyOn(predicate2, 'evaluate').and.returnValues(false, true);
+ const predicate3EvaluateSpy = spyOn(predicate3, 'evaluate').and.returnValues(true);
+ const comparableStub1 = new ComparableStub('F');
+ const comparableStub2 = new ComparableStub('G');
+ predicatesComparable = PredicatesComparable.of(predicate1, predicate2, predicate3);
+
+ // When
+ const r1 = predicatesComparable.equal(injectedComparable);
+ const r2 = predicatesComparable.equal(comparableStub1);
+ const r3 = predicatesComparable.equal(comparableStub2);
+
+ // Then
+ expect(r1).toBeFalse();
+ expect(predicate1EvaluateSpy.calls.argsFor(0)).toEqual([injectedComparable]);
+ expect(predicate2EvaluateSpy.calls.argsFor(0)).toEqual([injectedComparable]);
+
+ expect(r2).toBeFalse();
+ expect(predicate1EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub1]);
+
+ expect(r3).toBeTrue();
+ expect(predicate1EvaluateSpy.calls.argsFor(2)).toEqual([comparableStub2]);
+ expect(predicate2EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub2]);
+ expect(predicate3EvaluateSpy.calls.argsFor(0)).toEqual([comparableStub2]);
+
+ expect(predicate1EvaluateSpy).toHaveBeenCalledTimes(3);
+ expect(predicate2EvaluateSpy).toHaveBeenCalledTimes(2);
+ expect(predicate3EvaluateSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('|notEqual|', () => {
+ it('should verify will return true if one or more predicates return false, otherwise false', () => {
+ // Given
+ const predicate1EvaluateSpy = spyOn(predicate1, 'evaluate').and.returnValues(false, true, true);
+ const predicate2EvaluateSpy = spyOn(predicate2, 'evaluate').and.returnValues(false, true);
+ const predicate3EvaluateSpy = spyOn(predicate3, 'evaluate').and.returnValues(true);
+ const comparableStub1 = new ComparableStub('H');
+ const comparableStub2 = new ComparableStub('I');
+ predicatesComparable = PredicatesComparable.of(predicate1, predicate2, predicate3);
+
+ // When
+ const r1 = predicatesComparable.notEqual(injectedComparable);
+ const r2 = predicatesComparable.notEqual(comparableStub1);
+ const r3 = predicatesComparable.notEqual(comparableStub2);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(predicate1EvaluateSpy.calls.argsFor(0)).toEqual([injectedComparable]);
+
+ expect(r2).toBeTrue();
+ expect(predicate1EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub1]);
+ expect(predicate2EvaluateSpy.calls.argsFor(0)).toEqual([comparableStub1]);
+
+ expect(r3).toBeFalse();
+ expect(predicate1EvaluateSpy.calls.argsFor(2)).toEqual([comparableStub2]);
+ expect(predicate2EvaluateSpy.calls.argsFor(1)).toEqual([comparableStub2]);
+ expect(predicate3EvaluateSpy.calls.argsFor(0)).toEqual([comparableStub2]);
+
+ expect(predicate1EvaluateSpy).toHaveBeenCalledTimes(3);
+ expect(predicate2EvaluateSpy).toHaveBeenCalledTimes(2);
+ expect(predicate3EvaluateSpy).toHaveBeenCalledTimes(1);
+ });
+ });
+
+ describe('|lessThan|', () => {
+ it('should verify will return -1 and log warn to console', () => {
+ // When
+ const comparison = predicatesComparable.lessThan(injectedComparable);
+
+ // Then
+ expect(comparison).toBeFalse();
+ expect(consoleWarnSpy).toHaveBeenCalledWith('PredicatesComparable, unsupported comparison.');
+ });
+ });
+
+ describe('|lessThanInclusive|', () => {
+ it('should verify will return -1 and log warn to console', () => {
+ // When
+ const comparison = predicatesComparable.lessThanInclusive(injectedComparable);
+
+ // Then
+ expect(comparison).toBeFalse();
+ expect(consoleWarnSpy).toHaveBeenCalledWith('PredicatesComparable, unsupported comparison.');
+ });
+ });
+
+ describe('|greaterThan|', () => {
+ it('should verify will return -1 and log warn to console', () => {
+ // When
+ const comparison = predicatesComparable.greaterThan(injectedComparable);
+
+ // Then
+ expect(comparison).toBeFalse();
+ expect(consoleWarnSpy).toHaveBeenCalledWith('PredicatesComparable, unsupported comparison.');
+ });
+ });
+
+ describe('|greaterThanInclusive|', () => {
+ it('should verify will return -1 and log warn to console', () => {
+ // When
+ const comparison = predicatesComparable.greaterThanInclusive(injectedComparable);
+
+ // Then
+ expect(comparison).toBeFalse();
+ expect(consoleWarnSpy).toHaveBeenCalledWith('PredicatesComparable, unsupported comparison.');
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.ts
new file mode 100644
index 0000000000..af1f0109c9
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/comparable/predicates-comparable.impl.ts
@@ -0,0 +1,114 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../../utils';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+export class PredicatesComparable implements Comparable {
+
+ /**
+ * @inheritDoc
+ */
+ public readonly value: T;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(...predicates: T) {
+ this.value = predicates ?? [] as T;
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(...predicates: Predicate[]): PredicatesComparable {
+ return new PredicatesComparable(...predicates);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ compare(_comparable: Comparable): number {
+ console.warn('PredicatesComparable, unsupported comparison.');
+
+ return -1;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ isNil(): boolean {
+ return CollectionsUtil.isNil(this.value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ notNil(): boolean {
+ return CollectionsUtil.isDefined(this.value);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ like(comparable: Comparable): boolean {
+ return this.value
+ .some((predicate) => predicate.evaluate(comparable));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ equal(comparable: Comparable): boolean {
+ return this.value
+ .every((predicate) => predicate.evaluate(comparable));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ notEqual(comparable: Comparable): boolean {
+ return !this.value
+ .every((predicate) => predicate.evaluate(comparable));
+ }
+
+ /**
+ * @inheritDoc
+ */
+ lessThan(_comparable: Comparable): boolean {
+ return PredicatesComparable._defaultUnsupported();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ lessThanInclusive(_comparable: Comparable): boolean {
+ return PredicatesComparable._defaultUnsupported();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ greaterThan(_comparable: Comparable): boolean {
+ return PredicatesComparable._defaultUnsupported();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ greaterThanInclusive(_comparable: Comparable): boolean {
+ return PredicatesComparable._defaultUnsupported();
+ }
+
+ // eslint-disable-next-line @typescript-eslint/member-ordering
+ private static _defaultUnsupported(): boolean {
+ console.warn('PredicatesComparable, unsupported comparison.');
+
+ return false;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.spec.ts
new file mode 100644
index 0000000000..86a7e534eb
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.spec.ts
@@ -0,0 +1,120 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CallFake } from '../../../unit-testing';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from '../comparable';
+
+import { CompoundPredicate } from './base-compound.predicate';
+import { And } from './and.predicate';
+
+describe('And', () => {
+ it('should verify instance is created from one predicate', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = new And(predicate1);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ });
+
+ it('should verify instance is created from predicates', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = new And(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ expect(instance.comparable.value[1]).toBe(predicate2);
+ });
+
+ it('should verify instance is created from comparable', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const comparable = new PredicatesComparable(predicate1, predicate2);
+
+ // When
+ const instance = new And(comparable);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBe(comparable);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance from predicates', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = And.of(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeInstanceOf(And);
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ expect(instance.comparable.value[1]).toBe(predicate2);
+ });
+
+ it('should verify factory method will create instance from comparable', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const comparable = new PredicatesComparable(predicate1, predicate2);
+
+ // When
+ const instance = And.of(comparable);
+
+ // Then
+ expect(instance).toBeInstanceOf(And);
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBe(comparable);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|evaluate|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const comparableEqualSpy = spyOn(PredicatesComparable.prototype, 'equal').and.returnValue(true);
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicatesComparable = new PredicatesComparable(predicate1, predicate2);
+ const instance = new And(predicatesComparable);
+ const standardComparable = {} as Comparable;
+
+ // When
+ const result = instance.evaluate(standardComparable);
+
+ // Then
+ expect(result).toBeTrue();
+ expect(comparableEqualSpy).toHaveBeenCalledWith(standardComparable);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.ts
new file mode 100644
index 0000000000..efb876ecdc
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/and.predicate.ts
@@ -0,0 +1,35 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from '../comparable';
+
+import { CompoundPredicate } from './base-compound.predicate';
+
+export class And extends CompoundPredicate {
+
+ /**
+ * ** Factory method.
+ */
+ static override of(comparable: PredicatesComparable): And;
+ static override of(...predicates: Predicate[]): And;
+ static override of(...values: Predicate[] | [PredicatesComparable]): And {
+ if (values[0] instanceof PredicatesComparable) {
+ return new And(values[0]);
+ } else {
+ return new And(...values as Predicate[]);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ evaluate(comparable: Comparable): boolean {
+ return this.comparable.equal(comparable);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.spec.ts
new file mode 100644
index 0000000000..7153e2ff9c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.spec.ts
@@ -0,0 +1,28 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CallFake } from '../../../unit-testing';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { CompoundPredicate } from './base-compound.predicate';
+
+describe('CompoundPredicate', () => {
+ describe('Statics::', () => {
+ describe('|of|', () => {
+ it('should verify will throw Error', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When/Then
+ expect(() => CompoundPredicate.of(predicate1, predicate2))
+ .toThrowError('Method have to be overridden in Subclasses.');
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.ts
new file mode 100644
index 0000000000..40efaf16a4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/base-compound.predicate.ts
@@ -0,0 +1,46 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from '../comparable';
+
+export abstract class CompoundPredicate implements Predicate {
+ /**
+ * @inheritDoc
+ */
+ readonly comparable: PredicatesComparable;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(comparable: PredicatesComparable);
+ constructor(...predicates: Predicate[]);
+ constructor(...values: Predicate[] | [PredicatesComparable]) {
+ if (values.length === 1) {
+ if (values[0] instanceof PredicatesComparable) {
+ this.comparable = values[0];
+ } else {
+ this.comparable = PredicatesComparable.of(values[0]);
+ }
+ } else {
+ this.comparable = PredicatesComparable.of(...values as Predicate[]);
+ }
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(..._args: unknown[]): CompoundPredicate {
+ throw new Error('Method have to be overridden in Subclasses.');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ abstract evaluate(value: Comparable): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/index.ts
new file mode 100644
index 0000000000..45222c6bbf
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './base-compound.predicate';
+export * from './and.predicate';
+export * from './or.predicate';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.spec.ts
new file mode 100644
index 0000000000..a52a52cb98
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.spec.ts
@@ -0,0 +1,120 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CallFake } from '../../../unit-testing';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from '../comparable';
+
+import { CompoundPredicate } from './base-compound.predicate';
+import { Or } from './or.predicate';
+
+describe('Or', () => {
+ it('should verify instance is created from one predicate', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = new Or(predicate1);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ });
+
+ it('should verify instance is created from predicates', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = new Or(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ expect(instance.comparable.value[1]).toBe(predicate2);
+ });
+
+ it('should verify instance is created from comparable', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const comparable = new PredicatesComparable(predicate1, predicate2);
+
+ // When
+ const instance = new Or(comparable);
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBe(comparable);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance from predicates', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When
+ const instance = Or.of(predicate1, predicate2);
+
+ // Then
+ expect(instance).toBeInstanceOf(Or);
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBeInstanceOf(PredicatesComparable);
+ expect(instance.comparable.value[0]).toBe(predicate1);
+ expect(instance.comparable.value[1]).toBe(predicate2);
+ });
+
+ it('should verify factory method will create instance from comparable', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const comparable = new PredicatesComparable(predicate1, predicate2);
+
+ // When
+ const instance = Or.of(comparable);
+
+ // Then
+ expect(instance).toBeInstanceOf(Or);
+ expect(instance).toBeInstanceOf(CompoundPredicate);
+ expect(instance.comparable).toBe(comparable);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|evaluate|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const comparableLikeSpy = spyOn(PredicatesComparable.prototype, 'like').and.returnValue(true);
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicatesComparable = new PredicatesComparable(predicate1, predicate2);
+ const instance = new Or(predicatesComparable);
+ const standardComparable = {} as Comparable;
+
+ // When
+ const result = instance.evaluate(standardComparable);
+
+ // Then
+ expect(result).toBeTrue();
+ expect(comparableLikeSpy).toHaveBeenCalledWith(standardComparable);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.ts
new file mode 100644
index 0000000000..621bfd720b
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/compound/or.predicate.ts
@@ -0,0 +1,35 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { PredicatesComparable } from '../comparable';
+
+import { CompoundPredicate } from './base-compound.predicate';
+
+export class Or extends CompoundPredicate {
+
+ /**
+ * ** Factory method.
+ */
+ static override of(comparable: PredicatesComparable): Or;
+ static override of(...predicates: Predicate[]): Or;
+ static override of(...values: Predicate[] | [PredicatesComparable]): Or {
+ if (values[0] instanceof PredicatesComparable) {
+ return new Or(values[0]);
+ } else {
+ return new Or(...values as Predicate[]);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ evaluate(comparable: Comparable): boolean {
+ return this.comparable.like(comparable);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/index.ts
new file mode 100644
index 0000000000..8b47b57bfd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './comparable';
+export * from './compound';
+export * from './simple';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.spec.ts
new file mode 100644
index 0000000000..69a4f97aad
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.spec.ts
@@ -0,0 +1,28 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CallFake } from '../../../unit-testing';
+
+import { Comparable, Predicate } from '../../interfaces';
+
+import { SimplePredicate } from './base-simple.predicate';
+
+describe('SimplePredicate', () => {
+ describe('Statics::', () => {
+ describe('|of|', () => {
+ it('should verify will throw Error', () => {
+ // Given
+ const predicate1: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+ const predicate2: Predicate = { comparable: {} as Comparable, evaluate: CallFake };
+
+ // When/Then
+ expect(() => SimplePredicate.of(predicate1, predicate2))
+ .toThrowError('Method have to be overridden in Subclasses.');
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.ts
new file mode 100644
index 0000000000..ea7676fa72
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/base-simple.predicate.ts
@@ -0,0 +1,34 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable, Predicate } from '../../interfaces';
+
+export abstract class SimplePredicate implements Predicate {
+ /**
+ * @inheritDoc
+ */
+ readonly comparable: T;
+
+ /**
+ * ** Constructor.
+ */
+ protected constructor(comparable: T) {
+ this.comparable = comparable;
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(..._args: unknown[]): SimplePredicate {
+ throw new Error('Method have to be overridden in Subclasses.');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ abstract evaluate(comparable: T): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.spec.ts
new file mode 100644
index 0000000000..e9883a0d25
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.spec.ts
@@ -0,0 +1,82 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable } from '../../interfaces';
+
+import { ComparableImpl } from '../comparable';
+
+import { Equal } from './equal.predicate';
+
+describe('Equal', () => {
+ let v1: string;
+ let v2: string;
+ let v3: string;
+
+ let c1: Comparable;
+ let c2: Comparable;
+ let c3: Comparable;
+
+ beforeEach(() => {
+ v1 = 'Taurus';
+ v2 = 'Taurus';
+ v3 = 'VDK';
+
+ c1 = ComparableImpl.of(v1);
+ c2 = ComparableImpl.of(v2);
+ c3 = ComparableImpl.of(v3);
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const p = new Equal(c1);
+
+ // Then
+ expect(p).toBeDefined();
+ });
+
+ it('should verify value (Comparable) is correctly assigned', () => {
+ // When
+ const p = Equal.of(c1);
+
+ // Then
+ expect(p.comparable).toBe(c1);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::()', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const p = Equal.of(c1);
+
+ // Then
+ expect(p).toBeDefined();
+ expect(p).toBeInstanceOf(Equal);
+ });
+ });
+ });
+ });
+
+ describe('Methods::()', () => {
+ describe('|evaluate|', () => {
+ it('should verify will return true if both comparables have same values, otherwise false', () => {
+ // Given
+ const p = Equal.of(c1);
+
+ // When
+ const r1 = p.evaluate(c2);
+ const r2 = p.evaluate(c3);
+ const r3 = p.evaluate(c1);
+
+ // Then
+ expect(r1).toBeTrue();
+ expect(r2).toBeFalse();
+ expect(r3).toBeTrue();
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.ts
new file mode 100644
index 0000000000..2f2183cecb
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/equal.predicate.ts
@@ -0,0 +1,38 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Comparable } from '../../interfaces';
+
+import { SimplePredicate } from './base-simple.predicate';
+
+/**
+ * ** Equal Predicate that accepts Comparable and make equality evaluation.
+ *
+ *
+ */
+export class Equal extends SimplePredicate {
+ /**
+ * ** Constructor.
+ */
+ constructor(comparable: T) {
+ super(comparable);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(comparable: Comparable): Equal {
+ return new Equal(comparable);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ evaluate(comparable: Comparable): boolean {
+ return this.comparable.equal(comparable);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/index.ts
new file mode 100644
index 0000000000..5ee54f15ed
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/predicate/simple/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './base-simple.predicate';
+export * from './equal.predicate';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/public-api.ts
new file mode 100644
index 0000000000..1188800ed1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/public-api.ts
@@ -0,0 +1,13 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './http/public-api';
+export * from './interfaces/public-api';
+export * from './object/public-api';
+export * from './predicate/public-api';
+export * from './route/public-api';
+export * from './tasks/public-api';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/index.ts
new file mode 100644
index 0000000000..db0bb6637c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './route.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/route.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/route.model.ts
new file mode 100644
index 0000000000..68e3ffceec
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/route/route.model.ts
@@ -0,0 +1,118 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { Data, NavigationExtras, Params, Route } from '@angular/router';
+
+import { Replacer } from '../interfaces';
+
+/**
+ * ** Interface for Navigate back command.
+ */
+export interface TaurusNavigateAction {
+ /**
+ * ** Path / Path commands where user should be navigated, returned or redirected.
+ *
+ * - If path is '$.current' - current loaded path will be use.
+ * - If path is '$.requested' - requested path will be use.
+ * - If path is '$.parent' - parent of the current path will be use.
+ * - Any other string - use at is and assume it as path for navigation (URL path)
+ *
+ */
+ path: string | '$.current' | '$.requested' | '$.parent';
+
+ /**
+ * ** Replacers to tune and finalize navigate back Path / Path commands.
+ */
+ replacers?: Array>;
+
+ /**
+ * ** Optional query params for navigation action.
+ */
+ queryParams?: Params;
+
+ /**
+ * ** Optional instruction for queryParams handling.
+ *
+ * - If not provided will fallback to default one 'merge'.
+ * - If provided and its value is null, it won't do any handling for queryParams.
+ */
+ queryParamsHandling?: NavigationExtras['queryParamsHandling'];
+
+ /**
+ * ** Optional instruction whether to use resolved path or config path.
+ *
+ * - TRUE - will use config path, which means use it as it's configured in the routing modules.
+ * - FALSE - will use resolved path to the current point.
+ */
+ useConfigPath?: boolean;
+}
+
+/**
+ * ** Taurus Route data with navigate to command.
+ */
+export interface TaurusRouteNavigateToData {
+ /**
+ * ** Field that has Navigate to command.
+ */
+ navigateTo?: TaurusNavigateAction;
+}
+
+/**
+ * ** Taurus Route data with navigate back command.
+ */
+export interface TaurusRouteNavigateBackData {
+ /**
+ * ** Field that has Navigate back command.
+ */
+ navigateBack?: TaurusNavigateAction;
+}
+
+/**
+ * ** Taurus Route data with redirect command.
+ */
+export interface TaurusRouteRedirectData {
+ /**
+ * ** Field that has Redirect command.
+ */
+ redirect?: TaurusNavigateAction;
+}
+
+/**
+ * ** Custom type for Route Data with Generics.
+ */
+export type TaurusRouteData = Record> =
+ T
+ & TaurusRouteNavigateToData
+ & TaurusRouteNavigateBackData
+ & TaurusRouteRedirectData
+ & Data;
+
+/**
+ * ** Taurus Route config.
+ */
+export interface TaurusRoute extends Route {
+ /**
+ * @inheritDoc
+ *
+ * ** Static data configured per Route.
+ */
+ data?: T;
+
+ /**
+ * @inheritDoc
+ *
+ * ** Children Route configs object.
+ */
+ children?: TaurusRoutes;
+}
+
+/**
+ * ** Taurus Routes configs.
+ */
+export type TaurusRoutes = TaurusRoute[];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/index.ts
new file mode 100644
index 0000000000..a9552dbc58
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './task.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.spec.ts
new file mode 100644
index 0000000000..cf5f85815c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.spec.ts
@@ -0,0 +1,56 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../utils';
+
+import { createTaskIdentifier, extractTaskFromIdentifier } from './task.model';
+
+describe('Tasks', () => {
+ describe('|createTaskIdentifier|', () => {
+ it('should verify will return expected value', () => {
+ // Given
+ const task = 'create_entity';
+ const dateNowISO = new Date().toISOString();
+ const taskIdentifier = `${ task } __ ${ dateNowISO }`;
+ const dateISOSpy = spyOn(CollectionsUtil, 'dateISO').and.returnValue(dateNowISO);
+ const interpolateStringSpy = spyOn(CollectionsUtil, 'interpolateString').and.returnValue(taskIdentifier);
+ const expected = `${ task } __ ${ dateNowISO }`;
+
+ // When
+ const response = createTaskIdentifier(task);
+
+ // Then
+ expect(response).toEqual(expected);
+ expect(dateISOSpy).toHaveBeenCalled();
+ // @ts-ignore
+ expect(interpolateStringSpy).toHaveBeenCalledWith('%s __ %s', task, dateNowISO);
+ });
+
+ it('should verify will return value undefined when no task provided', () => {
+ // When
+ const response = createTaskIdentifier(null);
+
+ // Then
+ expect(response).toBeUndefined();
+ });
+ });
+
+ describe('|extractTaskFromIdentifier|', () => {
+ it('should verify will return Task from provided identifier', () => {
+ // Given
+ const dateNowISO = new Date().toISOString();
+ const taskIdentifier = `delete_entity __ ${ dateNowISO }`;
+ const expected = 'delete_entity';
+
+ // When
+ const response = extractTaskFromIdentifier(taskIdentifier);
+
+ // Then
+ expect(response).toEqual(expected);
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.ts
new file mode 100644
index 0000000000..08f450af50
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/common/tasks/task.model.ts
@@ -0,0 +1,37 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../utils';
+
+const TASK_IDENTIFIER_SEPARATOR = ' __ ';
+const TASK_IDENTIFIER_TEMPLATE = `%s${ TASK_IDENTIFIER_SEPARATOR }%s`;
+
+/**
+ * ** Factory for Tasks identifiers.
+ */
+export const createTaskIdentifier = (task: string) => {
+ if (CollectionsUtil.isString(task)) {
+ return CollectionsUtil.interpolateString(
+ TASK_IDENTIFIER_TEMPLATE,
+ task,
+ CollectionsUtil.dateISO()
+ );
+ }
+
+ return undefined;
+};
+
+/**
+ * ** Extract Task from Tasks identifiers.
+ */
+export const extractTaskFromIdentifier = (taskIdentifier: string) => {
+ if (CollectionsUtil.isString(taskIdentifier)) {
+ return taskIdentifier.split(TASK_IDENTIFIER_SEPARATOR)[0] as T;
+ }
+
+ return null;
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/colors.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/colors.scss
new file mode 100644
index 0000000000..707e22e936
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/colors.scss
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+$gray1: #747474;
+$gray2: #eee;
+$gray3: #ddd;
+$gray4: #ccc;
+$gray5: #8C8C8C;
+$black: #000;
+$black1: #313131;
+$error-red: #e62700;
+$error-red-dark-theme: #f54f47;
+$dark-text: #adbbc4;
+$red2: #c92100;
+$ready-green: #318700;
+$darkblue: rgb(0, 106, 144);
+$lighterBlue: #89CBDF;
+$lightblue: #49AFD9;
+$darkButtonHoverBlue: #0095D3;
+$blue3: #49afd9;
+$blue4: #57c7ea;
+$blue5: #0079b8;
+$inactiveGray: #95A4B2;
+$bgcolor: #25333D;
+$bgcolorDark: #0f171c;
+$bgcolorTextareaDark: #17242b;
+$white: #FFF;
+$focused-control-border: #6bc1e3;
+$dark-theme-text: #acbac3;
+$label-danger-color: #a32100;
+$label-success-color: #266900;
+$label-info-color: #004c8a;
+$clr-primary-color: #0072a3;
+$clr-primary-color-dark: #4aaed9;
+$clr-input-border-grey: #b3b3b3;
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/screen-sizes.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/screen-sizes.scss
new file mode 100644
index 0000000000..ca64f13dcf
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/screen-sizes.scss
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+$xxs-width: 321px;
+
+$xs-width: 543px;
+$xs-max: 543px;
+
+$small-min: 544px;
+$small-width: 767px;
+$small-max: 767px;
+
+$medium-min: 768px;
+$medium-width: 991px;
+$medium-width2: 990px;
+
+$large-min: 992px;
+$large-width: 1199px;
+
+$xl-min-width: 1600px;
+$xxl-min-width: 1919px;
+
+$min-el-size: 310px;
+
+.medium-and-up {
+ @media screen and (max-width: $medium-width2) {
+ display: none !important;
+ }
+}
+
+.small-and-up {
+ @media screen and (max-width: $xs-max) {
+ display: none !important;
+ }
+}
+
+.medium-and-down {
+ @media screen and (min-width: $medium-width) {
+ display: none !important;
+ }
+}
+
+.small-and-down {
+ @media screen and (min-width: $small-width) {
+ display: none !important;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/utils.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/utils.scss
new file mode 100644
index 0000000000..914f818af9
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/css/utils.scss
@@ -0,0 +1,39 @@
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * Theme-aware CSS utility
+ * Usage: Invoke mixin, including custom theme-specific CSS properties, inside braces `{ ... }`.
+ * Example:
+ *
+ * ```
+ * .page-example-image {
+ * width: 100px;
+ * height: 100px;
+ *
+ * @include light-theme {
+ * background-image: url('icon-light.png');
+ * border-color: black;
+ * }
+ * @include dark-theme {
+ * background-image: url('icon-dark.png');
+ * border-color: white;
+ * }
+ * }
+ * ```
+ */
+@mixin light-theme {
+ :host-context(body:not([cds-theme])) &,
+ :host-context(body[cds-theme='light']) & {
+ @content;
+ }
+}
+
+@mixin dark-theme {
+ :host-context(body[cds-theme='dark']) & {
+ @content;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/index.ts
new file mode 100644
index 0000000000..eb4cd3584c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './ngx-components';
+export * from './ngx-utils';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/animation-constants.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/animation-constants.ts
new file mode 100644
index 0000000000..8654e82ea5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/animation-constants.ts
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+// –——— CLARITY ANIMATIONS —————
+// ATOMIC Animations
+// primary
+export const atomicPrimaryEnterCurve = 'cubic-bezier(0, 1.5, 0.5, 1)';
+export const atomicPrimaryEnterTiming = 200;
+export const atomicPrimaryLeaveCurve = 'cubic-bezier(0,.99,0,.99)';
+export const atomicPrimaryLeaveTiming = 200;
+
+// secondary
+export const atomicSecondaryEnterCurve = 'cubic-bezier(0, 1.5, 0.5, 1)';
+export const atomicSecondaryEnterTiming = 400;
+export const atomicSecondaryLeaveCurve = 'cubic-bezier(0, 1.5, 0.5, 1)';
+export const atomicSecondaryLeaveTiming = 100;
+
+
+// COMPONENT Animations
+// primary
+export const componentPrimaryEnterCurve = 'cubic-bezier(0,.99,0,.99)';
+export const componentPrimaryEnterTiming = 400;
+export const componentPrimaryLeaveCurve = 'cubic-bezier(0,.99,0,.99)';
+export const componentPrimaryLeaveTiming = 300;
+
+// PAGE Animations
+// primary
+export const pagePrimaryEnterCurve = 'cubic-bezier(0,.99,0,.99)';
+export const pagePrimaryEnterTiming = 250;
+export const pagePrimaryLeaveCurve = 'cubic-bezier(0,.99,0,.99)';
+export const pagePrimaryLeaveTiming = 200;
+
+
+// PROGRESS Animations
+// primary
+export const progressPrimaryCurve = 'cubic-bezier(.17,.4,.8,.79)';
+export const progressPrimaryTiming = 790;
+
+// secondary
+export const progressSecondaryCurve = 'cubic-bezier(.34,.01,.39,1)';
+export const progressSecondaryTiming = 200;
+
+// ICON Animations
+// primary
+export const linePrimaryEnterCurve = 'linear';
+export const linePrimaryEnterTiming = 250;
+export const linePrimaryEnterDelay = 200;
+
+// secondary
+export const lineSecondaryEnterCurve = 'linear';
+export const lineSecondaryEnterTiming = 400;
+export const lineSecondaryEnterDelay = 200;
+
+// –——— NGX ONLY ANIMATIONS —————
+export const DISMISS_ICON_DURATION = 300;
+export const DISMISS_ICON_DELAY = 350;
+export const DISMISS_ICON_CURVE = 'cubic-bezier(0, 1.2, 0.7, 1)';
+
+export const GRADIENT_DURATION = 500;
+export const GRADIENT_DELAY = 100;
+export const GRADIENT_LEAVE_CURVE = 'cubic-bezier(0, 1.2, 0.7, 1)';
+export const STAGGER_DURATION = 200;
+
+// used for animation debugging
+const ANIMATION_MULTIPLIER = 1;
+
+export function multiply(value: number) {
+ return value * ANIMATION_MULTIPLIER;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.html
new file mode 100644
index 0000000000..449ab76ebe
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.html
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+ {{btnLabel}}
+
+
+
+
+
+
+
+
+
+ {{tooltip}}
+
+
+
+
+
+ {{copyAlert}}
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.scss
new file mode 100644
index 0000000000..9bd2f0a806
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.scss
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+span.copy-button {
+ button {
+ margin-right: 0px !important;
+ border: none;
+ padding: 0;
+ }
+}
+
+.flip-horizontal-bottom {
+ -webkit-animation: flip-horizontal-bottom 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
+ animation: flip-horizontal-bottom 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
+}
+
+.flip-horizontal-reverse {
+ -webkit-animation: flip-horizontal-reverse 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
+ animation: flip-horizontal-reverse 0.5s cubic-bezier(0.455, 0.030, 0.515, 0.955) both;
+}
+
+.anim-object {
+ transform-style: preserve-3d;
+ display: block;
+ outline: none;
+}
+
+.face {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ backface-visibility: hidden;
+ border-radius: 3px;
+ text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ transform: rotateX(0deg);
+}
+
+.front {
+ z-index: 20;
+
+ &.face-label {
+ position: relative;
+ }
+}
+
+.back {
+ z-index: 10;
+ transform: rotateX(180deg);
+ &.face-label {
+ top: 0px;
+ }
+}
+
+.hide-tooltip {
+ display: none;
+}
+
+/**
+ * ----------------------------------------
+ * animation flip-horizontal-bottom
+ * ----------------------------------------
+ */
+@-webkit-keyframes flip-horizontal-bottom {
+ 0% {
+ -webkit-transform: rotateX(0);
+ transform: rotateX(0);
+ }
+ 100% {
+ -webkit-transform: rotateX(-180deg);
+ transform: rotateX(-180deg);
+ }
+}
+
+@keyframes flip-horizontal-bottom {
+ 0% {
+ -webkit-transform: rotateX(0);
+ transform: rotateX(0);
+ }
+ 100% {
+ -webkit-transform: rotateX(-180deg);
+ transform: rotateX(-180deg);
+ }
+}
+
+@-webkit-keyframes flip-horizontal-reverse {
+ 0% {
+ -webkit-transform: rotateX(-180deg);
+ transform: rotateX(-180deg);
+ }
+ 100% {
+ -webkit-transform: rotateX(0);
+ transform: rotateX(0);
+ }
+}
+
+@keyframes flip-horizontal-reverse {
+ 0% {
+ -webkit-transform: rotateX(-180deg);
+ transform: rotateX(-180deg);
+ }
+ 100% {
+ -webkit-transform: rotateX(0);
+ transform: rotateX(0);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.spec.ts
new file mode 100644
index 0000000000..a1a034fac8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.spec.ts
@@ -0,0 +1,100 @@
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ViewChild, Component } from '@angular/core';
+import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
+
+import { ClarityModule } from "@clr/angular";
+
+import { VdkCopyToClipboardButtonComponent } from './copy-to-clipboard-button.component';
+
+describe('CopyToClipboardButtonComponent', () => {
+ let component: VdkCopyToClipboardButtonComponent;
+ let hostComponent: TestHostComponent;
+ let fixture: ComponentFixture;
+ let element: any;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ClarityModule,
+ ],
+ declarations: [
+ TestHostComponent,
+ VdkCopyToClipboardButtonComponent
+ ]
+ });
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TestHostComponent);
+ element = fixture.debugElement.nativeElement;
+ hostComponent = fixture.componentInstance;
+ component = hostComponent.component;
+ });
+
+ describe('when no btnLabel specified', () => {
+ beforeEach(() => {
+ fixture.detectChanges();
+ });
+
+ it('should initialize size properly', () => {
+ component.ngOnInit();
+ expect(component.bounds).toBe('22px');
+ component.size = 24;
+ component.ngOnInit();
+ expect(component.bounds).toBe('30px');
+ });
+
+ it('should copy the value to the clipboard', () => {
+ spyOn(component, 'copyToClipboard');
+ component.doCopy();
+ expect(component.copyToClipboard).toHaveBeenCalledWith('Test string');
+ });
+
+ it('should set the proper "copied" status', fakeAsync(() => {
+ component.doCopy();
+ expect(component.copied).toBeTruthy();
+ tick(1500);
+ expect(component.copied).toBeFalsy();
+ }));
+
+ // it('should show the copy icon', () => {
+ // expect(element).toHaveRendered('.copy-button');
+ // });
+
+ // it('should not show the copy button with text', () => {
+ // expect(element).not.toHaveRendered('button.btn.btn-outline');
+ // });
+ });
+
+ // describe('when btnLabel specified', () => {
+ // beforeEach(() => {
+ // component.btnLabel = 'COPY TO CLIPBOARD';
+ // fixture.detectChanges();
+ // });
+ //
+ // it('should show the copy button with text', () => {
+ // expect(element).toHaveRendered('button.btn.btn-outline');
+ //
+ // let label = element.querySelector('button .face-label span');
+ // expect(label.innerText.trim()).toEqual('COPY TO CLIPBOARD');
+ // });
+ // });
+});
+
+@Component({
+ template: `
+
+
+ `
+})
+class TestHostComponent {
+ @ViewChild(VdkCopyToClipboardButtonComponent, { static: true }) component: VdkCopyToClipboardButtonComponent;
+
+ public value = 'Test string';
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.ts
new file mode 100644
index 0000000000..7230cfaaff
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.component.ts
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ ElementRef,
+ HostBinding,
+ Output,
+ EventEmitter,
+ Component,
+ OnInit,
+ Input,
+ ViewChild,
+} from '@angular/core';
+import { VdkSimpleTranslateService } from '../../ngx-utils';
+
+import { TRANSLATIONS } from './copy-to-clipboard-button.l10n';
+
+const SHOW_CHECKBOX_TIMEOUT = 1500;
+
+@Component({
+ selector: 'vdk-copy-to-clipboard-button',
+ templateUrl: './copy-to-clipboard-button.component.html',
+ styleUrls: ['./copy-to-clipboard-button.component.scss']
+})
+export class VdkCopyToClipboardButtonComponent implements OnInit {
+ @ViewChild('area', { read: ElementRef, static: false }) area: ElementRef;
+
+ @HostBinding('class') @Input('class') classList: string = '';
+
+ @Input() value: string;
+ @Input() ariaLabel: string = "";
+ @Input() copyAlert: string;
+ @Input() size = 16;
+ @Input() tooltip = '';
+ @Input() btnLabel = ''; // if no label specified, show the normal copy icon
+ @Input() btnClasses = ['btn-outline']; // if no label specified, show the normal copy icon
+ @Input() disabled: boolean = false;
+ @Input() tooltipDirection = 'top-left';
+
+ @Output() copyClick = new EventEmitter();
+
+ private firstLoad = true; // show correct icon first
+
+ btnClassesToApply: string;
+ bounds: string;
+ hasProjectedContent: boolean = false;
+ isSafari: boolean = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
+ copied: boolean = false;
+
+ constructor(private el: ElementRef, public translateService: VdkSimpleTranslateService) {
+ this.translateService.loadTranslationsForComponent('copy-to-clipboard-button', TRANSLATIONS);
+ }
+
+ ngOnInit() {
+ this.bounds = (this.size + 6) + 'px';
+
+ this.hasProjectedContent = this.el.nativeElement.innerText.trim();
+
+ this.calculateClassesToApply();
+ this.copyAlert = this.copyAlert || this.translateService.translate('copy-to-clipboard-button.copied');
+ }
+
+ calculateClassesToApply() {
+ let classes: Array = [];
+
+ if (!this.btnLabel.length) {
+ classes.push('icon-btn');
+ }
+
+ if (this.btnLabel.length) {
+ classes = classes.concat(this.btnClasses);
+ }
+
+ if (this.disabled) {
+ classes.push('disabled');
+ }
+
+ this.btnClassesToApply = classes.join(' ') + ' ' + this.classList;
+ }
+
+ ngOnChanges() {
+ this.calculateClassesToApply();
+ }
+
+ copyToClipboard(val: string) {
+ let myWindow: any = window;
+
+ let onCopy = (e: ClipboardEvent) => {
+ e.preventDefault();
+
+ if (e.clipboardData) {
+ e.clipboardData.setData('text/plain', val);
+ } else if (myWindow.clipboardData) {
+ myWindow.clipboardData.setData('Text', val);
+ }
+
+ myWindow.removeEventListener('copy', onCopy);
+ };
+
+ if (this.isSafari) {
+ this.area.nativeElement.value = val;
+ this.area.nativeElement.select();
+ navigator.clipboard?.writeText(val);
+ }
+
+ myWindow.addEventListener('copy', onCopy);
+
+ if (myWindow.clipboardData && myWindow.clipboardData.setData) {
+ myWindow.clipboardData.setData('Text', val);
+ } else {
+ document.execCommand('copy');
+ }
+ }
+
+ doCopy() {
+ this.copyToClipboard(this.value);
+ this.copyClick.emit();
+ this.firstLoad = false;
+ this.copied = true;
+ setTimeout(() => {
+ this.copied = false;
+ }, SHOW_CHECKBOX_TIMEOUT);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.l10n.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.l10n.ts
new file mode 100644
index 0000000000..c8ef3cefc9
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/copy-to-clipboard-button.l10n.ts
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const TRANSLATIONS = {
+ 'en': {
+ 'copied': 'Copied'
+ },
+ 'de': {
+ 'copied': 'Kopiert'
+ },
+ 'es': {
+ 'copied': 'Copiado'
+ },
+ 'fr': {
+ 'copied': 'Copié'
+ },
+ 'ja': {
+ 'copied': 'コピー'
+ },
+ 'ko': {
+ 'copied': '복사'
+ },
+ 'zh_TW': {
+ 'copied': '已复制'
+ },
+ 'zh_CN': {
+ 'copied': '已復制'
+ },
+ 'it': {
+ 'copied': 'Copiato'
+ },
+ 'nl': {
+ 'copied': 'Gekopieerd'
+ },
+ 'pt': {
+ 'copied': 'Copiado'
+ },
+ 'ru': {
+ 'copied': 'Скопировано'
+ },
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/index.ts
new file mode 100644
index 0000000000..35266a99a0
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/index.ts
@@ -0,0 +1,16 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Type } from "@angular/core";
+
+import { VdkCopyToClipboardButtonComponent } from './copy-to-clipboard-button.component';
+
+export * from "./copy-to-clipboard-button.component";
+
+export const COPY_TO_CLIPBPOARD_BUTTON_DIRECTIVES: Type[] = [
+ VdkCopyToClipboardButtonComponent,
+];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/copy-to-clipboard-button/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.html
new file mode 100644
index 0000000000..d1aa65c6d4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+ {{title}}
+
+
+
+ {{description}}
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.scss
new file mode 100644
index 0000000000..7c65c41eae
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.scss
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+$text-dark: #919FA8;
+$text-light: #67747d;
+
+:host {
+ display: flex;
+ align-items: center;
+ flex-direction: column;
+ margin: 1rem 0;
+
+ cds-icon {
+ fill: #CCC;
+ }
+
+ .empty-placeholder-heading {
+ font-size: 18px;
+ color: $text-light;
+ margin-top: 18px;
+ line-height: 48px;
+ font-weight: 200;
+ }
+
+ .empty-placeholder-description {
+ margin-top: 0;
+ font-size: 13px;
+ color: $text-light;
+ margin-bottom: 18px;
+ font-weight: 200;
+ }
+}
+
+
+:host ::ng-deep button:last-child {
+ margin-right: 0 !important;
+}
+
+:host ::ng-deep button.btn-link {
+ margin-top: 0 !important;
+}
+
+:host-context(.dark) {
+ h2, h3 {
+ color: $text-dark;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.spec.ts
new file mode 100644
index 0000000000..674a0ed32a
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.spec.ts
@@ -0,0 +1,4 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.ts
new file mode 100644
index 0000000000..e96e5240a5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.component.ts
@@ -0,0 +1,20 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component, Input } from '@angular/core';
+
+@Component({
+ selector: 'vdk-empty-state-placeholder',
+ templateUrl: './empty-state-placeholder.component.html',
+ styleUrls: ["./empty-state-placeholder.component.scss"]
+})
+export class VdkEmptyStatePlaceholderComponent {
+ @Input('title') title: string;
+ @Input() icon: string;
+ @Input() description: string;
+ @Input() headingLevel = 2;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.module.ts
new file mode 100644
index 0000000000..d0d6729040
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/empty-state-placeholder.module.ts
@@ -0,0 +1,27 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CommonModule } from "@angular/common";
+import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from "@angular/core";
+import { VdkEmptyStatePlaceholderComponent } from "./empty-state-placeholder.component";
+
+@NgModule({
+ imports: [
+ CommonModule,
+ ],
+ declarations: [
+ VdkEmptyStatePlaceholderComponent,
+ ],
+ exports: [
+ VdkEmptyStatePlaceholderComponent,
+ ],
+ schemas: [
+ CUSTOM_ELEMENTS_SCHEMA,
+ ],
+})
+export class VdkEmptyStatePlaceholderModule {
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/index.ts
new file mode 100644
index 0000000000..2877606b57
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from "./empty-state-placeholder.component";
+export * from "./empty-state-placeholder.module";
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/empty-state-placeholder/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.html
new file mode 100644
index 0000000000..366023234d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.html
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.scss
new file mode 100644
index 0000000000..4e07a40b5f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.scss
@@ -0,0 +1,17 @@
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+:host ::ng-deep {
+
+ .section-title {
+ display: inline-block;
+ }
+}
+
+.csp-edit-button {
+ margin: 0 0;
+ cursor: pointer;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.spec.ts
new file mode 100644
index 0000000000..6606b01a94
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.spec.ts
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { VdkFormSectionContainerComponent } from './form-section-container.component';
+import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
+import { Component, DebugElement, ViewChild } from "@angular/core";
+import { ReactiveFormsModule, FormBuilder, FormGroup, FormControl } from '@angular/forms';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { By } from "@angular/platform-browser";
+
+import { ClarityModule } from "@clr/angular";
+
+import { FORM_STATE, VDKFormState } from './form-section-container.component';
+import { VdkFormSectionComponent } from '../form-section';
+import { VdkSimpleTranslateModule, VdkSimpleTranslateService } from "../../ngx-utils";
+
+let fixture: ComponentFixture;
+let hostComp: TestHostComponent;
+let page: PageObject;
+let el: Element;
+
+class PageObject {
+ fixtureElement: DebugElement;
+
+ constructor() {
+ }
+
+ populateLocators(): void {
+ this.fixtureElement = fixture.debugElement.query(By.css("vdk-form-section-container"));
+ }
+
+ getReadOnlySectionElements(): NodeListOf {
+ return el.querySelectorAll(".form-section-readonly");
+ }
+
+ getEditSectionElements(): NodeListOf {
+ return el.querySelectorAll(".form-section-edit");
+ }
+
+ setInputValue(inputName: string, inputValue: any) {
+ hostComp.editProfileForm.controls[inputName].setValue(inputValue);
+ hostComp.editProfileForm.controls[inputName].markAsDirty();
+ }
+
+ getSaveBtnSpan(): HTMLElement | undefined {
+ const span = fixture.debugElement.queryAll(By.css('.csp-save-button span'));
+ if (span && span.length > 0) {
+ return span[0].nativeElement;
+ }
+ return undefined;
+ }
+}
+
+@Component({
+ template: `
+ `
+
+})
+class TestHostComponent {
+ @ViewChild(VdkFormSectionContainerComponent, { static: false }) child: VdkFormSectionContainerComponent;
+ formState: VDKFormState;
+
+ editProfileForm = new FormGroup({
+ "firstName": new FormControl(),
+ });
+
+}
+
+describe("Form Section Container Component", () => {
+ function createComponent() {
+ fixture = TestBed.createComponent(TestHostComponent);
+ hostComp = fixture.componentInstance;
+ el = fixture.nativeElement;
+ fixture.detectChanges();
+ page = new PageObject();
+ page.populateLocators();
+ }
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ReactiveFormsModule,
+ VdkSimpleTranslateModule,
+ BrowserAnimationsModule,
+ ClarityModule,
+ ],
+ providers: [
+ VdkSimpleTranslateService,
+ FormBuilder
+ ],
+ declarations: [
+ TestHostComponent,
+ VdkFormSectionComponent,
+ VdkFormSectionContainerComponent,
+ ]
+ });
+
+ createComponent();
+ fixture.detectChanges();
+ }));
+
+ it("should project .title content correctly", () => {
+ expect(el.querySelector('.section-title').textContent).toContain('Title');
+ });
+
+ it("should readOnly and Edit sections to be toggled on Edit", () => {
+ expect(page.getReadOnlySectionElements().length).toBe(1, "Readonly Section shown initially");
+ expect(page.getEditSectionElements().length).toBe(0, "Edit section hidden initially");
+ expect(el.querySelectorAll(".csp-edit-button").length).toBe(1, "Edit button shown initially");
+ const editButton: HTMLInputElement = el.querySelector(".csp-edit-button");
+ editButton.click();
+ fixture.detectChanges();
+
+ expect(page.getReadOnlySectionElements().length).toBe(0, "Readonly section hidden on Edit button");
+ expect(page.getEditSectionElements().length).toBe(1, "Edit section shown on Edit button");
+ });
+
+ it("should readOnly and Edit sections to be toggled on Cancel", () => {
+ const editButton: HTMLInputElement = el.querySelector(".csp-edit-button");
+ editButton.click();
+ fixture.detectChanges();
+
+ expect(el.querySelectorAll(".csp-cancel-button").length).toBe(1, "Cancel button to be shown");
+ const cancelButton: HTMLInputElement = el.querySelector(".csp-cancel-button");
+ cancelButton.click();
+ fixture.detectChanges();
+
+ expect(page.getReadOnlySectionElements().length).toBe(1, "Readonly section shown on Cancel");
+ expect(page.getEditSectionElements().length).toBe(0, "Edit section hidden on Cancel");
+ });
+
+
+ it("should show Readonly and Edit section after save", () => {
+ const editButton: HTMLInputElement = el.querySelector(".csp-edit-button");
+ editButton.click();
+ fixture.detectChanges();
+
+ page.setInputValue('firstName', 'firstName');
+ expect(el.querySelectorAll(".csp-save-button").length).toBe(1, "Save button shown");
+ const saveButton: HTMLInputElement = el.querySelector(".csp-save-button");
+ expect(page.getSaveBtnSpan().innerText.trim()).toBe('Save', "save btn shown");
+ saveButton.click();
+ fixture.detectChanges();
+
+ expect(saveButton.disabled).toEqual(true, "Save to be disabled");
+ hostComp.formState = new VDKFormState(FORM_STATE.CAN_EDIT);
+ fixture.detectChanges();
+
+ expect(page.getReadOnlySectionElements().length).toBe(1, "Readonly Section shown");
+ expect(page.getEditSectionElements().length).toBe(0, "Edit Section hidden");
+ });
+
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.ts
new file mode 100644
index 0000000000..f736632652
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/form-section-container.component.ts
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ Component,
+ Output,
+ EventEmitter,
+ Input,
+ Host,
+ Optional,
+ ChangeDetectionStrategy,
+ ChangeDetectorRef,
+ ViewChild,
+ ElementRef
+} from "@angular/core";
+import { FormGroupDirective } from "@angular/forms";
+import { ClrLoadingState } from "@clr/angular";
+
+export enum FORM_STATE {
+ VIEW,
+ CAN_EDIT,
+ EDIT,
+ ERROR,
+ SUBMIT
+}
+export class VDKFormState {
+ state: FORM_STATE;
+
+ /**
+ * Optional. The section with this name identifier will be excluded from the state change.
+ */
+ emittingSection: string;
+
+ /**
+ * Optional.
+ * All the sections in the array will change its state.
+ * All the others will be excluded if this array is not empty.
+ */
+ sectionsToInclude: string[];
+
+ constructor( _state: FORM_STATE, _sectionsToInclude?: string[], _emittingSection?: string ) {
+ this.state = _state;
+ this.sectionsToInclude = _sectionsToInclude ? _sectionsToInclude : [];
+ this.emittingSection = _emittingSection;
+ }
+}
+
+@Component({
+ selector: "vdk-form-section-container",
+ templateUrl: "./form-section-container.component.html",
+ styleUrls: ['form-section-container.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class VdkFormSectionContainerComponent {
+ FORM_STATE = FORM_STATE; //used in the template
+ _sectionState: FORM_STATE = FORM_STATE.CAN_EDIT;
+ ClrLoadingState = ClrLoadingState;
+ stopInitialFocus = true;
+
+ @Input() canEditSection = true; //set to false if the section is readonly
+ @Input() isSubmitEnabled: boolean; //controls the Submit(Save) button state
+ @Input() sectionName: string; //unique section identifier
+ @Input() editBtn = "Edit"; //Edit button text
+ @Input() cancelBtn = "Cancel"; //Cancel button text
+ @Input() saveBtn = "Save"; //Save button text
+ @Input() editBtnAriaLabel = "Edit"; //Edit button text
+ @Input() cancelBtnAriaLabel = "Cancel"; //Cancel button text
+ @Input() saveBtnAriaLabel = "Save"; //Save button text
+
+ @Input('formState') set formState(_formState:VDKFormState) {
+ if (!_formState) {
+ return;
+ }
+
+ if ( (_formState.emittingSection && _formState.emittingSection !== this.sectionName) ||
+ ( !_formState.emittingSection && _formState.sectionsToInclude.length === 0 ) ||
+ (_formState.sectionsToInclude.some(name => name === this.sectionName)) ) {
+
+ //on ERROR set EDIT state only on submitted section
+ //to the rest of sections restore CAN_EDIT state
+ if (_formState.state === FORM_STATE.ERROR) {
+ if (this._sectionState === FORM_STATE.SUBMIT) {
+ this._sectionState = FORM_STATE.EDIT;
+ //put it in a microtask(make it asynchronous) to not violate detection run
+ //and avoid error for Expression changed after check
+ Promise.resolve(null).then(() => {
+ if (this.cspForm) {
+ this.cspForm.form.enable();
+ } else {
+ this.enableForm.emit();
+ }
+ });
+ } else {
+ this._sectionState = FORM_STATE.CAN_EDIT;
+ }
+ } else {
+ this.changeSectionState(_formState.state);
+ }
+ }
+ }
+
+ @Output() formStateChange = new EventEmitter();
+ @Output() sectionStateChange = new EventEmitter();
+
+ //Events that are used when no formGroup is found in parent component
+ @Output() submitForm = new EventEmitter();
+ @Output() disableForm = new EventEmitter();
+ @Output() enableForm = new EventEmitter();
+
+ @ViewChild('editButton') editButtonEl: ElementRef;
+
+ constructor(@Optional() @Host() private cspForm: FormGroupDirective, private cdr: ChangeDetectorRef) {}
+
+ showEditBtn() {
+ return this._sectionState === FORM_STATE.CAN_EDIT && this.canEditSection;
+ }
+
+ showSaveBtn() {
+ return this._sectionState === FORM_STATE.EDIT;
+ }
+
+ clickEdit() {
+ this.formStateChange.emit(new VDKFormState(FORM_STATE.CAN_EDIT, [], this.sectionName));
+ if (this.cspForm) {
+ this.cspForm.form.enable();
+ } else {
+ this.enableForm.emit();
+ }
+ this.changeSectionState(FORM_STATE.EDIT);
+ }
+
+ clickCancel() {
+ this.changeSectionState(FORM_STATE.CAN_EDIT);
+ }
+
+ // @ts-ignore
+ clickSave(): boolean {
+ this.formStateChange.emit(new VDKFormState(FORM_STATE.VIEW, [], this.sectionName));
+ this.changeSectionState(FORM_STATE.SUBMIT);
+ if (this.cspForm) {
+ this.cspForm.form.disable();
+ this.cspForm.onSubmit(this.cspForm.value);
+ this.cspForm.form.markAsPristine();
+ // cancel submitting the form since cspForm.onSubmit has been called above.
+ // solves a specific issue in Firefox where onSubmit was called twice.
+ return false;
+ } else {
+ this.disableForm.emit();
+ this.submitForm.emit();
+ }
+ }
+
+ changeSectionState(_sectionState: FORM_STATE) {
+ if (_sectionState !== this._sectionState) {
+ this.sectionStateChange.emit(new VDKFormState(_sectionState, [], this.sectionName));
+ this._sectionState = _sectionState;
+ }
+ }
+
+ focusEdit() {
+ if (this.editButtonEl && this._sectionState === FORM_STATE.CAN_EDIT && !this.stopInitialFocus) {
+ this.editButtonEl.nativeElement.focus();
+ }
+ if (this.stopInitialFocus) {
+ this.stopInitialFocus = false;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/index.ts
new file mode 100644
index 0000000000..dc87efc967
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/index.ts
@@ -0,0 +1,16 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Type } from "@angular/core";
+
+import { VdkFormSectionContainerComponent } from './form-section-container.component';
+
+export * from "./form-section-container.component";
+
+export const FORM_SECTION_CONTAINER_DIRECTIVES: Type[] = [
+ VdkFormSectionContainerComponent,
+];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section-container/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.html
new file mode 100644
index 0000000000..aea32aea2f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.html
@@ -0,0 +1,18 @@
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.scss
new file mode 100644
index 0000000000..8ff5bd4b2d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.scss
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+.vdk-form-container {
+ display: flex;
+
+ .vdk-vertical-line {
+ margin-top: 20px;
+ margin-bottom: 5px;
+ border-left: 3px solid #007cbb;
+ }
+
+ .vdk-form-content {
+
+ .form-header {
+ padding-bottom: 15px;
+ }
+
+ display: flex;
+ flex-direction: column;
+ width: 100%;
+ }
+
+ .form-header {
+ color:#000;
+ font-size:18px;
+ font-weight:200;
+ margin-block-start:24px;
+ margin-top:24px;
+ }
+
+ :host-context(.dark) {
+ .form-header {
+ color: #eaedf0;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.spec.ts
new file mode 100644
index 0000000000..267942c3b1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.spec.ts
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ComponentFixture, TestBed, waitForAsync } from "@angular/core/testing";
+import { Component, ViewChild } from "@angular/core";
+
+import { ClarityModule } from "@clr/angular";
+
+import { VdkFormSectionComponent } from "./form-section.component";
+import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
+
+@Component({
+ template: `
+
+
+ Test content
+
+
+ `
+})
+class TestHostComponent {
+ @ViewChild(VdkFormSectionComponent, { static: true }) child: VdkFormSectionComponent;
+ focused: boolean = false;
+}
+
+describe("VdkFormSectionComponent", () => {
+
+ let fixture: ComponentFixture;
+ let comp: VdkFormSectionComponent;
+ let el: any;
+
+ function createComponent() {
+ fixture = TestBed.createComponent(TestHostComponent);
+ comp = fixture.componentInstance.child;
+ el = fixture.debugElement.nativeElement;
+ fixture.detectChanges();
+ }
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ BrowserAnimationsModule,
+ ClarityModule,
+ ],
+ providers: [],
+ declarations: [
+ TestHostComponent,
+ VdkFormSectionComponent
+ ]
+ });
+
+ createComponent();
+ fixture.detectChanges();
+ }));
+
+ it("should project .form-section-header content correctly", () => {
+ const headers: NodeListOf = el.querySelectorAll("vdk-form-section span.form-section-header");
+ expect(headers.length).toBe(1);
+ });
+
+ it("should project .form-section-content content correctly", () => {
+ const content: NodeListOf = el.querySelectorAll("vdk-form-section span.form-section-content");
+ expect(content.length).toBe(1);
+ });
+
+ it("should project .form-section-footer content only when focused and then change edit state", () => {
+ expect(comp.getFormState()).toBe("normal");
+ const footer: NodeListOf = el.querySelectorAll("vdk-form-section span.form-section-footer");
+ expect(footer.length).toBe(0);
+ fixture.componentInstance.focused = true;
+ fixture.detectChanges();
+ expect(comp.getFormState()).toBe("edit");
+ const footers: NodeListOf = el.querySelectorAll("vdk-form-section span.form-section-footer");
+ expect(footers.length).toBe(1);
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.ts
new file mode 100644
index 0000000000..e05e30ed44
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/form-section.component.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component, EventEmitter, Input, Output } from "@angular/core";
+import { animate, state, style, transition, trigger } from "@angular/animations";
+
+
+@Component({
+ selector: 'vdk-form-section',
+ styleUrls: ['form-section.component.scss'],
+ templateUrl: 'form-section.component.html',
+ animations: [
+ trigger('customFormState', [
+ state('edit', style({
+ opacity: 1,
+ width: '3px',
+ 'margin-right': '24px'
+ })),
+ state('normal', style({
+ opacity: 0,
+ width: '0px',
+ 'margin-right': '0px'
+
+ })),
+ transition('normal => edit', [
+ animate('300ms ease-in-out')
+ ]),
+ transition('edit => normal', [
+ animate('100ms ease-in-out')
+ ])
+ ]),
+ trigger('footerState', [
+ transition(':enter', [
+ style({ opacity: 0, height: '0' }),
+ animate('0.1s 0.2s ease-in-out', style({ opacity: 1, height: '*'})),
+ ]),
+ transition(':leave', [
+ animate('0.1s', style({ opacity: 0, height: '0' }))
+ ])
+ ])
+ ]
+})
+
+export class VdkFormSectionComponent {
+ @Input()
+ focused: boolean = false;
+
+ @Output()
+ animationDone = new EventEmitter();
+
+ getFormState() {
+ return this.focused ? 'edit' : 'normal';
+ }
+
+ emitAnimationDone() {
+ this.animationDone.emit();
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/index.ts
new file mode 100644
index 0000000000..7b17da1cbf
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/index.ts
@@ -0,0 +1,16 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Type } from "@angular/core";
+
+import { VdkFormSectionComponent } from './form-section.component';
+
+export * from "./form-section.component";
+
+export const FORM_SECTION_DIRECTIVES: Type[] = [
+ VdkFormSectionComponent,
+];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/public-api.ts
new file mode 100644
index 0000000000..4996b4ff68
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/form-section/public-api.ts
@@ -0,0 +1,9 @@
+
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/index.ts
new file mode 100644
index 0000000000..4bc9385849
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/index.ts
@@ -0,0 +1,16 @@
+
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from "./copy-to-clipboard-button/index";
+export * from "./form-section/index";
+export * from "./form-section-container/index";
+export * from "./empty-state-placeholder/index";
+export * from "./toast/index";
+export * from "./search/index";
+
+export * from "./vdk-ngx-components.module";
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/index.ts
new file mode 100644
index 0000000000..cfb0e28f62
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/index.ts
@@ -0,0 +1,7 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './search.component'
+export { VdkSearchModule } from './search.module';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.html
new file mode 100644
index 0000000000..c33a6cad4c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.html
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ searchButtonText }}
+
+
+
+
+ {{helperText}}
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.scss
new file mode 100644
index 0000000000..1daa216d82
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.scss
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+:host {
+ width: 200px;
+ display: block;
+}
+
+.search-container {
+ display: flex;
+ flex-direction: column;
+ position: relative;
+ width: 100%;
+ margin-bottom: 0;
+ margin: 0.5rem 0;
+
+ &.disabled {
+ cursor: not-allowed;
+
+ * {
+ pointer-events: none;
+ }
+
+ input {
+ color: #888888;
+ }
+
+ cds-icon[shape="search"] {
+ fill: #888888;
+ }
+
+ .clear-search-btn {
+ cds-icon[shape="times-circle"] {
+ fill: #888888;
+ }
+ }
+ }
+
+ cds-icon[shape="search"] {
+ height: 18px;
+ position: absolute;
+ top: 3px;
+ left: 0;
+ }
+
+ &.focused {
+ cds-icon[shape="search"] {
+ fill: var(--clr-forms-focused-color);
+ stroke: var(--clr-forms-focused-color);
+ stroke-width: 0.75px;
+ }
+ }
+
+ .clear-search-btn {
+ position: absolute;
+ top: -1px;
+ right: 0;
+ padding: 0;
+ border: transparent;
+ background-color: transparent;
+
+ cds-icon[shape="times-circle"] {
+ fill: #565656;
+ opacity: 0.8;
+ &:hover {
+ cursor: pointer;
+ opacity: 1;
+ }
+ }
+ }
+
+ .btn-link {
+ position: absolute;
+ right: 0;
+ top: -6px;
+ margin: 0;
+ }
+
+ input {
+ padding: 0 24px;
+ flex: 1 1 24px;
+ }
+}
+
+:host-context(.dark) {
+ .clear-search-btn {
+ color: #d3d3d3;
+
+ cds-icon[shape="times-circle"] {
+ fill: #acbac3;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.spec.ts
new file mode 100644
index 0000000000..af4d373825
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.spec.ts
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ComponentFixture, TestBed, ComponentFixtureAutoDetect, waitForAsync } from '@angular/core/testing';
+import { ReactiveFormsModule } from "@angular/forms";
+
+import { ClarityModule } from "@clr/angular";
+import { VdkSearchComponent } from "./search.component";
+
+let comp: VdkSearchComponent;
+let fixture: ComponentFixture;
+let page: PageObject;
+export class PageObject {
+
+ getSearchInputElement(): HTMLInputElement {
+ return fixture.nativeElement.querySelector(`[data-test-id="search-input"]`);
+ }
+
+ getSearchButtonElement(): HTMLInputElement {
+ return fixture.nativeElement.querySelector(`[data-test-id="search-button"]`);
+ }
+
+ getClearSearchBtn(): HTMLButtonElement {
+ return fixture.nativeElement.querySelector(`[data-test-id="clear-search-btn"]`);
+ }
+
+ getSearchIcon(): HTMLElement {
+ return fixture.nativeElement.querySelector(`[data-test-id="search-icon"]`);
+ }
+
+ getHelperText(): HTMLElement {
+ return fixture.nativeElement.querySelector('[data-test-id="search-results-text"]')
+ }
+}
+
+describe('VdkSearchComponent', () => {
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ ClarityModule,
+ ReactiveFormsModule
+ ],
+ declarations: [
+ VdkSearchComponent
+ ],
+ providers: [
+ { provide: ComponentFixtureAutoDetect, useValue: true },
+ ]
+ });
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(VdkSearchComponent);
+ comp = fixture.componentInstance;
+ fixture.detectChanges();
+ page = new PageObject();
+ });
+
+ it('should create', () => {
+ expect(comp).toBeTruthy();
+ expect(page.getSearchInputElement()).toBeTruthy();
+ expect(page.getSearchIcon()).toBeTruthy();
+ expect(page.getClearSearchBtn()).toBeFalsy();
+ });
+
+ it('should show and hide "clear search button"', waitForAsync(() => {
+ const searchQuery = 'test';
+ expect(page.getClearSearchBtn()).toBeFalsy();
+ page.getSearchInputElement().value = searchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(comp.searchQuery.value).toBe(searchQuery, 'searchQuery input value is not correct');
+ expect(comp.searchQueryValue).toBe(searchQuery, 'searchQueryValue property is not correct');
+ expect(page.getClearSearchBtn()).toBeTruthy();
+ page.getClearSearchBtn().click();
+ fixture.detectChanges();
+ return fixture.whenStable();
+ })
+ .then(() => {
+ expect(page.getClearSearchBtn()).toBeFalsy();
+ });
+ }));
+
+ it('should input show correct data', waitForAsync(() => {
+ const searchQuery = 'test';
+ page.getSearchInputElement().value = searchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(page.getSearchInputElement().value).toBe(searchQuery);
+ page.getClearSearchBtn().click();
+ fixture.detectChanges();
+ return fixture.whenStable();
+ })
+ .then(() => {
+ expect(comp.searchQueryValue).toBe('');
+ });
+ }));
+
+ it('should emit correct value', waitForAsync(() => {
+ const searchQuery = 'test';
+ const searchSpy = spyOn(comp.search, 'emit').and.callThrough();
+ page.getSearchInputElement().value = searchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(searchSpy).toHaveBeenCalledWith(searchQuery);
+ });
+ }));
+
+
+ it('should show helper text with results when resultCount is a number', () => {
+ fixture.componentInstance.helperText = `Over 9999 results`;
+ fixture.detectChanges();
+ expect(page.getHelperText()).not.toBeNull();
+ });
+
+ it('should not show helper text with results when resultCount is not used', () => {
+ expect(page.getHelperText()).toBeNull();
+ });
+
+ describe('when in "Manual Search" mode', () => {
+ beforeEach(() => {
+ comp.showSearchButton = true;
+ });
+
+ it(
+ 'should show "SEARCH" button disabled when more than one char is entered but it is not above the min limit',
+ waitForAsync(async () => {
+ comp.searchTermMinimalLength = 3;
+
+ fixture.detectChanges();
+
+ expect(page.getSearchButtonElement()).toBeNull();
+
+ page.getSearchInputElement().value = 't';
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ let searchButton = page.getSearchButtonElement();
+ expect(searchButton).toBeTruthy();
+ expect(searchButton.disabled).toBeTruthy();
+
+ page.getSearchInputElement().value = 'two';
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ searchButton = page.getSearchButtonElement();
+ expect(searchButton).toBeTruthy();
+ expect(searchButton.disabled).toBeFalsy();
+ })
+ );
+
+ it(
+ 'should emit value ONLY upon clicking "Enter" or "SEARCH" and should hide the latter',
+ waitForAsync(async () => {
+ const searchQuery = 'test';
+ const searchSpy = spyOn(comp.search, 'emit').and.callThrough();
+ page.getSearchInputElement().value = searchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(page.getClearSearchBtn()).toBeFalsy();
+ expect(searchSpy).not.toHaveBeenCalledWith(searchQuery);
+
+ const searchButton = page.getSearchButtonElement();
+ searchButton.click();
+
+ await fixture.whenStable();
+
+ expect(searchSpy).toHaveBeenCalledWith(searchQuery);
+
+ expect(page.getClearSearchBtn()).toBeTruthy();
+
+ page.getSearchInputElement().dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
+
+ const newSearchQuery = searchQuery + '123';
+ page.getSearchInputElement().value = newSearchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(searchSpy).toHaveBeenCalledWith(newSearchQuery);
+ })
+ );
+
+ it(
+ 'should clear input upon clicking "X" button',
+ waitForAsync(async () => {
+ const searchQuery = 'test';
+ const searchSpy = spyOn(comp.search, 'emit').and.callThrough();
+
+ page.getSearchInputElement().value = searchQuery;
+ page.getSearchInputElement().dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(searchSpy).not.toHaveBeenCalledWith(searchQuery);
+
+ let searchButton = page.getSearchButtonElement();
+ searchButton.click();
+ await fixture.whenStable();
+
+ expect(searchSpy).toHaveBeenCalledWith(searchQuery);
+
+ searchButton = page.getSearchButtonElement();
+ expect(searchButton).toBeNull();
+
+ const clearSearchButton = page.getClearSearchBtn();
+ expect(clearSearchButton).toBeTruthy();
+
+ clearSearchButton.click();
+ fixture.detectChanges();
+ await fixture.whenStable();
+
+ expect(comp.searchQueryValue).toEqual('');
+ expect(searchSpy).toHaveBeenCalledWith('');
+ })
+ );
+ });
+
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.ts
new file mode 100644
index 0000000000..64cbe04d00
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.component.ts
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Component, ElementRef, EventEmitter, Input, Output, ViewChild } from "@angular/core";
+import { FormControl } from "@angular/forms";
+
+import { BehaviorSubject, combineLatest, Subject, Subscription } from "rxjs";
+import { debounceTime } from "rxjs/operators";
+
+const defaultSearchInputPadding = 24;
+
+@Component({
+ selector: "vdk-search",
+ templateUrl: "./search.component.html",
+ styleUrls: ["./search.component.scss"],
+})
+export class VdkSearchComponent {
+ searchInputPaddingRight = `${defaultSearchInputPadding}px`;
+
+ private _disabled: boolean = false;
+ public get disabled(): boolean {
+ return this._disabled;
+ }
+ @Input('disabled')
+ public set disabled(value: boolean) {
+ this._disabled = value;
+
+ if (value) {
+ this.searchQuery?.disable({ emitEvent: false });
+ } else {
+ this.searchQuery?.enable({ emitEvent: false });
+ }
+ }
+
+ @Input() searchQueryValue: string = '';
+ @Input() clearSearchTitle: string = 'Clear Search';
+ @Input('placeholder')
+ set placeholder(pass: string) {
+ this.finalPlaceholder = pass ? pass : "Search";
+ }
+
+ @Input() helperText: string = '';
+ @Input() debounceTime: number = 100;
+ @Input() searchTermMinimalLength: number = 2;
+ @Input() showSearchButton = false;
+ @Input() searchButtonText: string = "Search";
+ @Input() searchButtonAriaLabelText: string = "Search";
+ @Input() searchAriaLabelText: string;
+
+ @Output() search: EventEmitter = new EventEmitter();
+
+ @ViewChild('searchButton', { read: ElementRef })
+ searchButton?: ElementRef;
+ isSeachButtonVisible: boolean;
+
+ private triggerSearch$: Subject = new BehaviorSubject(undefined);
+ private hasSearchBeenTriggeredManually: boolean;
+
+ public searchQuery: FormControl;
+ public searchQuerySub: Subscription;
+ public focused: boolean = false;
+ public finalPlaceholder: string = "Search";
+
+ ngOnInit() {
+ this.searchQuery = new FormControl(this.searchQueryValue);
+
+ this.searchQuerySub = combineLatest([this.searchQuery.valueChanges, this.triggerSearch$])
+ .pipe(debounceTime(this.debounceTime))
+ .subscribe(([query]) => {
+ const queryLength = query.length;
+ query = query.trim();
+ this.searchQueryValue = query;
+
+ // not emit search event if it hasn't been inputted something different from whitespace
+ if (this.searchQueryValue.length === 0 &&
+ queryLength !== 0) {
+ return;
+ }
+
+ // Make sure that the 'Search' button will be visible in 'Manual Search' mode upon every change.
+ this.isSeachButtonVisible = this.showSearchButton;
+
+ const shouldNotifyForQueryChange = !this.showSearchButton || this.hasSearchBeenTriggeredManually;
+ const inputHasMinLengthOrIsCleared =
+ this.searchQueryValue.length >= this.searchTermMinimalLength || this.searchQueryValue.length === 0;
+ if (shouldNotifyForQueryChange && inputHasMinLengthOrIsCleared) {
+ // If we are about to notify that the search term has changed replace 'Search' button with the `X` one.
+ this.isSeachButtonVisible = false;
+ this.search.emit(query);
+ }
+
+ this.hasSearchBeenTriggeredManually = false;
+ this.computeSearchInputPadding();
+ });
+ }
+
+ ngOnDestroy(): void {
+ if (this.searchQuerySub) {
+ this.searchQuerySub.unsubscribe();
+ }
+ }
+
+ clearSearch(): void {
+ this.searchQuery.setValue('');
+ if (this.showSearchButton) {
+ this.triggerSearch();
+ }
+ }
+
+ handleKeyDown(event: KeyboardEvent): void {
+ if (event.key === 'Enter') {
+ this.triggerSearch();
+ }
+ }
+
+ triggerSearch(): void {
+ this.hasSearchBeenTriggeredManually = true;
+ this.triggerSearch$.next();
+ }
+
+ private computeSearchInputPadding(): void {
+ if (this.showSearchButton && this.isSeachButtonVisible) {
+
+ // Wait for the search button to be rendered as changes 'shouldShowSearchButton' might not be applied in the template.
+ // Useful especially after the first rendering.
+ setTimeout(() => {
+ this.searchInputPaddingRight =
+ Math.round(this.searchButton?.nativeElement.clientWidth || defaultSearchInputPadding) + 'px';
+ });
+ } else {
+ this.searchInputPaddingRight = `${defaultSearchInputPadding}px`;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.module.ts
new file mode 100644
index 0000000000..e705868a39
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/search/search.module.ts
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
+
+import { CommonModule } from "@angular/common";
+import {VdkSearchComponent} from "./search.component";
+import {ReactiveFormsModule} from "@angular/forms";
+
+@NgModule({
+ declarations: [
+ VdkSearchComponent
+ ],
+ imports: [
+ CommonModule,
+ ReactiveFormsModule
+ ],
+ exports: [
+ VdkSearchComponent
+ ],
+ schemas: [
+ CUSTOM_ELEMENTS_SCHEMA,
+ ],
+})
+export class VdkSearchModule {}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/index.ts
new file mode 100644
index 0000000000..6a02961b9a
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/index.ts
@@ -0,0 +1,18 @@
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Type } from "@angular/core";
+import { VdkToastComponent } from "./toast.component";
+import { VdkToastContainerComponent } from "./toast-container.component";
+
+export * from "./toast.component";
+export * from "./toast-container.component";
+export * from "./toast.model";
+
+export const TOAST_DIRECTIVES: Type[] = [
+ VdkToastContainerComponent,
+ VdkToastComponent,
+];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.html
new file mode 100644
index 0000000000..de5c2d9785
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.html
@@ -0,0 +1,8 @@
+
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.scss
new file mode 100644
index 0000000000..f3f25c1356
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.scss
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+@import "../../css/screen-sizes.scss";
+
+:host {
+ display: block;
+ z-index: 1040;
+ position: absolute;
+ right: 0;
+ width: 414px;
+ opacity: 1.0;
+ margin-top: 18px;
+
+ @media screen and(max-width: $xs-width) {
+ max-width: $min-el-size;
+ right: -12px;
+ }
+}
+
+.toast-container {
+ margin-right: 0;
+ > div {
+ margin-left: 0px;
+ margin-right: -12px;
+ }
+ display: flex;
+ justify-content: flex-end;
+ flex-direction: column;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.ts
new file mode 100644
index 0000000000..6036f5fea3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast-container.component.ts
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { HostBinding, Input, Component } from '@angular/core';
+import {
+ trigger,
+ transition,
+ query,
+ animateChild,
+ stagger,
+} from '@angular/animations';
+
+import {
+ multiply,
+ STAGGER_DURATION,
+} from "../animation-constants";
+
+import { VdkToastComponent } from './toast.component';
+
+@Component({
+ selector: "vdk-toast-container",
+ templateUrl: "./toast-container.component.html",
+ styleUrls: ["./toast-container.component.scss"],
+ animations:[
+ trigger('toastContainer', [
+ transition(':enter', [
+ query('@launchToast', [
+ stagger(`${multiply(STAGGER_DURATION)}ms`, animateChild())
+ ], {optional: true}),
+ ]),
+ transition(':leave', [
+ query('@launchToast', [
+ animateChild()
+ ], {optional:true})
+ ]),
+ ])
+ ]
+})
+export class VdkToastContainerComponent {
+ @Input() topOffset: number = 0;
+
+ @HostBinding('style.top')
+ get top(): string {
+ return (60 + this.topOffset) + 'px';
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.html b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.html
new file mode 100644
index 0000000000..2acec5f5e3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.html
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{primaryButtonText}}
+
+
+ {{secondaryButtonText}}
+
+
+
+
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.scss b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.scss
new file mode 100644
index 0000000000..0f24bc0dd7
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.scss
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+@import "../../css/colors";
+@import "../../css/screen-sizes.scss";
+
+:host {
+ @media screen and(max-width: $xs-width) {
+ max-width: 75vw;
+ }
+
+ ::ng-deep .toast-description {
+ font-size: 1.0em;
+ color: #ADBBC4;
+ margin-top: 3px;
+ line-height: 18px;
+
+ -webkit-line-clamp: 4;
+ -moz-line-clamp: 4;
+ display: -webkit-box;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ // https://github.com/postcss/autoprefixer/issues/776
+ /* autoprefixer: ignore next */
+ -webkit-box-orient: vertical;
+ max-height: 70px;
+ }
+
+ ::ng-deep .toast-link {
+ font-size: 0.85em;
+ color: #89CBDF;
+ display: block;
+ text-transform: uppercase;
+ font-family: 'Metropolis-Semibold';
+ margin-top: 12px;
+ float: right;
+ letter-spacing: 1px;
+ }
+
+ ::ng-deep .toast-date {
+ color: #ADBBC4;
+ font-size: 11px;
+ margin-left: 6px;
+ position: relative;
+ top: 1px;
+ }
+
+ ::ng-deep .toast-title {
+ font-weight: 500;
+ color: #E9ECEF;
+ font-size: 14px;
+ margin-top: 0;
+ line-height: 24px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ max-width: 240px;
+ }
+
+}
+
+.toast {
+ display: flex;
+ flex-direction: row;
+ max-width: 100%;
+ min-width: $min-el-size;
+ background-color: $bgcolor;
+ border-radius: 3px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4);
+ position: relative;
+ right: 18px;
+ padding: 12px;
+ margin: 0 0 12px 0;
+
+ // animation - steady state of component
+ transform: scale(1, 1);
+ transform-origin: 100% 0;
+}
+
+.dismiss-bg {
+ .dismiss {
+ cds-icon[shape="times"] {
+ fill: #ADBBC4;
+ margin-top: -8px;
+ margin-left: 1px;
+ }
+ }
+ background: transparent;
+ width: 30px;
+ height: 30px;
+ display: inline-block;
+ position: absolute;
+ right: 8px;
+ top: 8px;
+ cursor: pointer;
+ border-radius: 100%;
+ border: none;
+
+ &:hover {
+ background-color: rgba(0, 0, 0, 0.15);
+ }
+}
+
+
+.dismiss {
+ position: absolute;
+ left: 6px;
+ top: 6px;
+}
+
+
+.gradient {
+ width: 35%;
+ height: 100%;
+ background: linear-gradient(-90deg, rgba($bgcolor, 0) -14.71%, rgba($bgcolor, 1) 46.71%);
+ display: inline-block;
+ position: absolute;
+ top: 0px;
+ transform: scale(0, 1);
+ transform-origin: 0 100%;
+}
+
+.toast-button {
+ font-weight: 500;
+ color: #89CBDF;
+ text-transform: uppercase;
+ font-size: 11px;
+ letter-spacing: 1px;
+ margin-top: 12px;
+}
+
+.toast-button.secondary {
+ margin-right: 6px;
+}
+
+button.toast-button {
+ background: none;
+ border: none;
+}
+
+.toast-title-container {
+ display: flex;
+ flex-direction: row;
+}
+
+div.clr-row {
+ padding-bottom: 10px;
+}
+
+div.content {
+ padding-left: 12px;
+ width: 100%;
+}
+
+div.content-wrapper {
+ flex: 1;
+ display: flex;
+}
+
+div.button-container {
+ text-align: right;
+
+ button {
+ cursor: pointer;
+
+ &:hover {
+ color: $darkButtonHoverBlue;
+ }
+ }
+}
+
+// icons
+.icon {
+ transform: scale(1);
+ transform-origin: center;
+}
+
+.icon-container {
+ margin: -6px;
+}
+
+// info
+#info-icon-outline, #info-icon-line {
+ fill: none;
+ stroke: #0095D3;
+ stroke-miterlimit: 10;
+ stroke-width: 2;
+}
+
+#info-icon-line {
+ stroke-linecap: round;
+ stroke-dasharray: 16;
+ stroke-dashoffset: 0;
+ animation-fill-mode: forwards;
+}
+
+#info-icon-dot {
+ transform-origin: 50% 42%;
+ fill: #0095D3;
+}
+
+//warning
+#warn-icon-line, #warn-icon-triangle, #warn-icon-dot {
+ fill: #ffef5ff1;
+}
+
+#warn-icon-dot {
+ transform-origin: 50% 54%;
+}
+
+// error
+#error-icon-outline, #error-icon-line {
+ fill: none;
+ stroke: #F54F47;
+ stroke-miterlimit: 10;
+ stroke-width: 2;
+}
+
+#error-icon-line {
+ stroke-linecap: round;
+ stroke-width: 2.65;
+ stroke-dasharray: 7;
+ animation-fill-mode: forwards;
+ stroke-dashoffset: 0;
+}
+
+#error-icon-dot {
+ transform-origin: 50% 54%;
+ fill: #F54F47;
+}
+
+// success icon
+.checkmark {
+ fill: none;
+ stroke: #FFFFFF;
+ stroke-linecap: round;
+ stroke-miterlimit: 10;
+ stroke-width: 1.9px;
+ stroke-dasharray: 31.386688232421875;
+ animation-fill-mode: forwards;
+}
+
+:host-context(.dark) {
+ .toast {
+ background-color: $bgcolorDark;
+ }
+
+ .gradient {
+ background: linear-gradient(-90deg, rgba($bgcolorDark, 0) -14.71%, rgba($bgcolorDark, 1) 46.71%);
+ }
+
+ ::ng-deep .toast-description {
+ color: #A9B6BE;
+ }
+
+ ::ng-deep .toast-button {
+ color: #49AFD9;
+
+ &:hover {
+ color: #0095D3 ;
+ }
+ }
+
+ ::ng-deep .toast-date {
+ color: #A9B6BE;
+ }
+
+ .dismiss-bg:hover {
+ background-color: rgba(255,255,255,.1);
+ }
+}
+
+// MUTED styles
+:host-context(.muted) {
+ ::ng-deep .toast-title,
+ ::ng-deep .toast-date,
+ ::ng-deep .toast-description,
+ ::ng-deep .button-container button {
+ color: $inactiveGray;
+ }
+
+ .info-bg,
+ .error-bg {
+ fill: $inactiveGray;
+ }
+
+ .info-dot {
+ fill: $bgcolor;
+ }
+
+ .info-bg,
+ .info-dot,
+ .info-line-vertical,
+ .info-line-horizontal,
+ .error-bg,
+ .error-line,
+ .error-dot {
+ stroke: $bgcolor;
+ }
+
+ :host-context(.dark) {
+ .info-bg,
+ .info-dot,
+ .info-line-vertical,
+ .info-line-horizontal,
+ .error-bg,
+ .error-line,
+ .error-dot {
+ stroke: $bgcolorDark;
+ }
+
+ .info-dot {
+ fill: $bgcolorDark;
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.ts
new file mode 100644
index 0000000000..8b6358ff34
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.component.ts
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ ElementRef,
+ NgZone,
+ Component,
+ Input,
+ Output,
+ EventEmitter,
+ HostListener,
+ Optional
+} from "@angular/core";
+import {
+ trigger,
+ group,
+ style,
+ animate,
+ transition,
+ query,
+ animateChild,
+ keyframes,
+} from '@angular/animations';
+
+import { timer } from "rxjs";
+import { take } from "rxjs/operators";
+
+import { VmwToastType } from "./toast.model";
+import { TRANSLATIONS } from './toast.l10n';
+import { VdkSimpleTranslateService } from "../../ngx-utils";
+
+import {
+ multiply,
+ componentPrimaryEnterCurve,
+ componentPrimaryEnterTiming,
+ componentPrimaryLeaveCurve,
+ componentPrimaryLeaveTiming,
+ linePrimaryEnterCurve,
+ linePrimaryEnterTiming,
+ linePrimaryEnterDelay,
+ lineSecondaryEnterCurve,
+ lineSecondaryEnterTiming,
+ lineSecondaryEnterDelay,
+ DISMISS_ICON_DELAY,
+ DISMISS_ICON_DURATION,
+ DISMISS_ICON_CURVE,
+ GRADIENT_DURATION,
+ GRADIENT_DELAY,
+ GRADIENT_LEAVE_CURVE,
+} from "../animation-constants";
+
+const AUTODISMISS_TIMEOUT_SECONDS = 6;
+const TRACKED_TAG = {
+ "A": true,
+ "BUTTON": true
+}
+
+@Component({
+ selector: "vdk-toast",
+ templateUrl: "./toast.component.html",
+ styleUrls: ["./toast.component.scss"],
+ animations: [
+ trigger('launchToast', [
+ transition(':enter', [
+ // toast parent element animation
+ group([
+ style({
+ transform: 'translateX(48px) scale(0, 1)'
+ }),
+ animate(`${multiply(componentPrimaryEnterTiming)}ms ${componentPrimaryEnterCurve}`, style({
+ transform: 'translateX(0) scale(1, 1)'
+ })),
+
+ // use optional: true for if/else elements
+ query('.checkmark', animateChild(), {optional: true}),
+ query('#info-icon-dot', animateChild(), {optional: true}),
+ query('#info-icon-line', animateChild(), {optional: true}),
+ query('#warn-icon-dot', animateChild(), {optional: true}),
+ query('#warn-icon-line', animateChild(), {optional: true}),
+ query('#error-icon-dot', animateChild(), {optional: true}),
+ query('#error-icon-line', animateChild(), {optional: true}),
+ query('.gradient', animateChild()),
+ query('.dismiss', animateChild(), { optional: true }),
+ ]),
+ ]),
+
+ // START LEAVE ANIMATION
+ // ':leave' is a default state for ngIf and ngFor, doesn't need to be predefined
+ transition(':leave', [
+ group([
+ style({
+ transform: 'translateX(0px) scale(1, 1)',
+ marginTop: '*',
+ }),
+
+ // use query self to be able to group the animation on the current element
+ query(':self', [
+ animate(`${multiply(componentPrimaryLeaveTiming)}ms ${componentPrimaryLeaveCurve}`, style({
+ transform: 'translateX(18px) scale(0, 1)'
+ })),
+
+ animate(`${multiply(componentPrimaryLeaveTiming)}ms ${componentPrimaryLeaveCurve}`, style({
+ marginTop: '-{{height}}px'
+ })),
+ ]),
+
+ query('.toast-description, .toast-title, .icon, .button-container, .dismiss-bg, .dismiss, .toast-date', [
+ animate(`${multiply(10)}ms`, style({
+ opacity: '0'
+ })),
+ ]),
+
+ ]),
+ ], {
+ params: {
+ height: 0
+ }
+ }),
+ // end launchToast
+ ]),
+
+
+ // info icon animation
+ trigger('infoLine', [
+ transition('* => *', [
+ animate(`${multiply(linePrimaryEnterTiming)}ms ${multiply(linePrimaryEnterDelay)}ms ${linePrimaryEnterCurve}`, keyframes([
+ style({ strokeDashoffset: '16', offset: 0 }),
+ style({ strokeDashoffset: '0', offset: 1.0}),
+ ]))
+ ]),
+ ]),
+ trigger('infoDot', [
+ transition('* => *', [
+ style({
+ transform: 'scale(0)'
+ }),
+ animate(`${multiply(lineSecondaryEnterTiming)}ms ${multiply(lineSecondaryEnterDelay)}ms ${lineSecondaryEnterCurve}`, style({
+ transform: 'scale(1)'
+ }))
+ ]),
+ ]),
+
+ // error icon animation
+ trigger('errorLine', [
+ transition('* => *', [
+ animate(`${multiply(linePrimaryEnterTiming)}ms ${multiply(linePrimaryEnterDelay)}ms ${linePrimaryEnterCurve}`, keyframes([
+ style({ strokeDashoffset: '7.919999599456787', offset: 0 }),
+ style({ strokeDashoffset: '0', offset: 1.0}),
+ ]))
+ ]),
+ ]),
+ trigger('errorDot', [
+ transition('* => *', [
+ style({
+ transform: 'scale(0)'
+ }),
+ animate(`${multiply(lineSecondaryEnterTiming)}ms ${multiply(lineSecondaryEnterDelay)}ms ${lineSecondaryEnterCurve}`, style({
+ transform: 'scale(1)'
+ }))
+ ]),
+ ]),
+
+ //warning icon animation
+ trigger('warnLine', [
+ transition('* => *', [
+ animate(`${multiply(linePrimaryEnterTiming)}ms ${multiply(linePrimaryEnterDelay)}ms ${linePrimaryEnterCurve}`, keyframes([
+ style({ strokeDashoffset: '7.919999599456787', offset: 0 }),
+ style({ strokeDashoffset: '0', offset: 1.0}),
+ ]))
+ ]),
+ ]),
+ trigger('warnDot', [
+ transition('* => *', [
+ style({
+ transform: 'scale(0)'
+ }),
+ animate(`${multiply(lineSecondaryEnterTiming)}ms ${multiply(lineSecondaryEnterDelay)}ms ${lineSecondaryEnterCurve}`, style({
+ transform: 'scale(1)'
+ }))
+ ]),
+ ]),
+
+ // success icon animation
+ trigger('checkmarkLine', [
+ transition('* => *', [
+ // css keyframe animation
+ animate(`${multiply(linePrimaryEnterTiming)}ms ${multiply(linePrimaryEnterDelay)}ms ${linePrimaryEnterCurve}`, keyframes([
+ style({ strokeDashoffset: '31.386688232421875', offset: 0 }),
+ style({ strokeDashoffset: '0', offset: 1.0}),
+ ]))
+ ]),
+ ]),
+
+ // moving the gradient offview
+ trigger('gradientMove', [
+ transition('* => *', [
+ style({
+ transform: 'scale(1, 1)'
+ }),
+ animate(`${multiply(GRADIENT_DURATION)}ms ${multiply(GRADIENT_DELAY)}ms ${GRADIENT_LEAVE_CURVE}`, style({
+ transform: 'scale(0, 1)'
+ }))
+ ]),
+ ]),
+
+ // fade in the dismiss icon
+ trigger('dismissIconVisible', [
+ transition('* => *', [
+ style({
+ opacity: '0'
+ }),
+ animate(`${multiply(DISMISS_ICON_DURATION)}ms ${multiply(DISMISS_ICON_DELAY)}ms ${DISMISS_ICON_CURVE}`, style({
+ opacity: '1'
+ }))
+ ]),
+ ]),
+ ]
+
+})
+export class VdkToastComponent {
+ public mouseover = false;
+ public focused = false;
+
+ @Input() type: VmwToastType = VmwToastType.INFO;
+ @Input() primaryButtonText: string;
+ @Input() secondaryButtonText: string;
+ @Input() dismissible: boolean = true;
+ @Input() timeoutSeconds: number = AUTODISMISS_TIMEOUT_SECONDS;
+
+ @Output() dismissed = new EventEmitter();
+ @Output() primaryButtonClick = new EventEmitter();
+ @Output() secondaryButtonClick = new EventEmitter();
+
+ readonly VmwToastType = VmwToastType;
+ disableAutoDismiss: boolean = false;
+ height: number;
+ animate = true;
+
+ constructor(
+ private element: ElementRef,
+ private ngZone: NgZone,
+ public translateService: VdkSimpleTranslateService,
+ // @Optional() private segmentService,
+ ) {
+ this.translateService.loadTranslationsForComponent('toast', TRANSLATIONS);
+ }
+
+ ngOnInit() {
+ this.setUpTimer();
+ }
+
+ @HostListener('click', ['$event'])
+ trackClicks(event: any) {
+ return;
+ }
+
+ mouseOver(over: boolean) {
+ // If the user moves their mouse over the snack, disable auto-dismiss
+ this.disableAutoDismiss = over;
+ }
+
+ focus(focused: boolean) {
+ this.disableAutoDismiss = focused;
+ }
+
+ get loaded() {
+ return {
+ value: this.animate,
+ params: {
+ height: this.element.nativeElement.clientHeight
+ }
+ };
+ }
+
+ dismiss(userDismissed: boolean = false) {
+ this.animate = false;
+
+ // before we tell the app to remove the toast, give the leave animation
+ // some time to run...
+ timer(multiply(componentPrimaryLeaveTiming + 200)).pipe(take(1)).subscribe(() => {
+ this.dismissed.emit();
+ });
+ }
+
+ private setUpTimer() {
+ if (this.timeoutSeconds > 0) {
+ this.ngZone.runOutsideAngular(() => {
+ timer(this.timeoutSeconds * multiply(1000)).pipe(take(1)).subscribe(() => {
+ this.ngZone.run(() => {
+ if (this.disableAutoDismiss) {
+ this.setUpTimer();
+ return;
+ }
+ this.dismiss();
+ });
+ });
+ });
+ }
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.l10n.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.l10n.ts
new file mode 100644
index 0000000000..d54ac54fbe
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.l10n.ts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export const TRANSLATIONS = {
+ 'en': {
+ 'dismiss-notification': 'Dismiss notification',
+ 'success-icon': 'Success Icon',
+ 'info-icon': 'Information Icon',
+ 'failure-icon': 'Failure Icon',
+ },
+ 'es': {
+ 'dismiss-notification': 'Descartar notificación',
+ 'success-icon': 'Icono de éxito',
+ 'failure-icon': 'Icono de error',
+ 'info-icon': 'Icono de información',
+ },
+ 'de': {
+ 'dismiss-notification': 'Benachrichtigung verwerfen',
+ 'success-icon': 'Symbol „Erfolgreich“',
+ 'failure-icon': 'Fehlersymbol',
+ 'info-icon': 'Informationssymbol',
+ },
+ 'fr': {
+ 'dismiss-notification': 'Ignorer la notification',
+ 'success-icon': 'Icône de réussite',
+ 'failure-icon': "Icône d'échec",
+ 'info-icon': "Icône d'informations",
+ },
+ 'ja': {
+ 'dismiss-notification': '通知を破棄',
+ 'success-icon': '成功アイコン',
+ 'failure-icon': '障害アイコン',
+ 'info-icon': '情報アイコン',
+ },
+ 'ko': {
+ 'dismiss-notification': '알림 해제',
+ 'success-icon': '성공 아이콘',
+ 'failure-icon': '실패 아이콘',
+ 'info-icon': '정보 아이콘',
+ },
+ 'zh_TW': {
+ 'dismiss-notification': '關閉通知',
+ 'success-icon': '成功图标',
+ 'failure-icon': '失敗圖示',
+ 'info-icon': '資訊圖示',
+ },
+ 'zh_CN': {
+ 'dismiss-notification': '关闭通知',
+ 'success-icon': '成功图标',
+ 'info-icon': '信息图标',
+ 'failure-icon': '故障图标',
+ },
+ 'it': {
+ 'dismiss-notification': 'Ignora notifica',
+ 'success-icon': 'Icona Operazione riuscita',
+ 'info-icon': 'Icona Informazioni',
+ 'failure-icon': 'Icona Errore',
+ },
+ 'nl': {
+ 'dismiss-notification': 'Melding negeren',
+ 'success-icon': 'Pictogram Geslaagd',
+ 'info-icon': 'Pictogram Informatie',
+ 'failure-icon': 'Pictogram Fout',
+ },
+ 'pt': {
+ 'dismiss-notification': 'Descartar notificação',
+ 'success-icon': 'Ícone de êxito',
+ 'info-icon': 'Ícone de informação',
+ 'failure-icon': 'Ícone de falha',
+ },
+ 'ru': {
+ 'dismiss-notification': 'Закрыть уведомление',
+ 'success-icon': 'Значок успешного выполнения',
+ 'info-icon': 'Значок информации',
+ 'failure-icon': 'Значок ошибки',
+ },
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.model.ts
new file mode 100644
index 0000000000..ca492ec7d2
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/toast/toast.model.ts
@@ -0,0 +1,11 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export enum VmwToastType {
+ SUCCESS = "success",
+ FAILURE = "failure",
+ INFO = "info",
+ WARN = "warning",
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/vdk-ngx-components.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/vdk-ngx-components.module.ts
new file mode 100644
index 0000000000..3d200de559
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-components/vdk-ngx-components.module.ts
@@ -0,0 +1,101 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders, NgModule } from "@angular/core";
+import { CommonModule } from "@angular/common";
+import { FormsModule, ReactiveFormsModule } from "@angular/forms";
+
+import {
+ ClarityModule,
+ ClrDropdownModule,
+ ClrLoadingButtonModule,
+ ClrLoadingModule,
+ ClrTooltipModule,
+} from "@clr/angular";
+
+import { VdkSimpleTranslateModule } from '../ngx-utils';
+
+import {
+ angleIcon,
+ arrowIcon,
+ checkCircleIcon,
+ checkIcon,
+ ClarityIcons,
+ copyToClipboardIcon,
+ exclamationCircleIcon,
+ searchIcon,
+ timesCircleIcon,
+ timesIcon
+} from '@cds/core/icon';
+
+import { VdkEmptyStatePlaceholderModule } from "./empty-state-placeholder/empty-state-placeholder.module";
+
+import { COPY_TO_CLIPBPOARD_BUTTON_DIRECTIVES } from "./copy-to-clipboard-button/index";
+import { FORM_SECTION_DIRECTIVES } from "./form-section/index";
+import { FORM_SECTION_CONTAINER_DIRECTIVES } from "./form-section-container/index";
+import { TOAST_DIRECTIVES } from "./toast/index";
+import { VdkSearchModule } from "./search";
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ ReactiveFormsModule,
+ ClarityModule,
+ ClrTooltipModule,
+ ClrDropdownModule,
+ ClrLoadingModule,
+ ClrLoadingButtonModule,
+ VdkSimpleTranslateModule,
+ VdkEmptyStatePlaceholderModule,
+ VdkSearchModule
+ ],
+ declarations: [
+ COPY_TO_CLIPBPOARD_BUTTON_DIRECTIVES,
+ FORM_SECTION_DIRECTIVES,
+ FORM_SECTION_CONTAINER_DIRECTIVES,
+ TOAST_DIRECTIVES
+ ],
+ exports: [
+ COPY_TO_CLIPBPOARD_BUTTON_DIRECTIVES,
+ FORM_SECTION_DIRECTIVES,
+ FORM_SECTION_CONTAINER_DIRECTIVES,
+ TOAST_DIRECTIVES,
+ VdkEmptyStatePlaceholderModule,
+ VdkSearchModule
+ ],
+ schemas: [
+ CUSTOM_ELEMENTS_SCHEMA,
+ ],
+})
+export class VdkComponentsModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: VdkComponentsModule,
+ };
+ }
+
+ static forChild(): ModuleWithProviders {
+ return {
+ ngModule: VdkComponentsModule,
+ };
+ }
+
+ constructor() {
+ ClarityIcons.addIcons(
+ angleIcon,
+ arrowIcon,
+ checkCircleIcon,
+ checkIcon,
+ copyToClipboardIcon,
+ exclamationCircleIcon,
+ searchIcon,
+ timesCircleIcon,
+ timesIcon
+ );
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/index.ts
new file mode 100644
index 0000000000..79cbf723dd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/index.ts
@@ -0,0 +1,6 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from "./simple-translate-service/index";
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/index.ts
new file mode 100644
index 0000000000..b135809eb5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/index.ts
@@ -0,0 +1,8 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from "./simple-translate.module";
+export * from "./simple-translate.service";
+export * from "./simple-translate.pipe";
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.module.ts
new file mode 100644
index 0000000000..4aef87e8f8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.module.ts
@@ -0,0 +1,30 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NgModule, ModuleWithProviders } from "@angular/core";
+
+import { VdkSimpleTranslateService } from "./simple-translate.service";
+import { VdkSimpleTranslatePipe } from "./simple-translate.pipe";
+
+@NgModule({
+ declarations: [
+ VdkSimpleTranslatePipe,
+ ],
+ exports: [
+ VdkSimpleTranslatePipe,
+ ],
+})
+export class VdkSimpleTranslateModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: VdkSimpleTranslateModule,
+ providers: [
+ VdkSimpleTranslateService,
+ ]
+ };
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.pipe.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.pipe.ts
new file mode 100644
index 0000000000..39f00b5294
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.pipe.ts
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Pipe, PipeTransform } from '@angular/core';
+
+import { VdkSimpleTranslateService } from './simple-translate.service';
+
+@Pipe({
+ name: 'simpleTranslate',
+ pure: false,
+})
+export class VdkSimpleTranslatePipe implements PipeTransform {
+ constructor(private simpleTranslate: VdkSimpleTranslateService) {}
+
+ transform(text: string, ...args: any[]) {
+ return this.simpleTranslate.translate(text, ...args);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.spec.ts
new file mode 100644
index 0000000000..47c9ac9eef
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.spec.ts
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { TestBed, inject } from "@angular/core/testing";
+
+import { VdkSimpleTranslateService } from "./simple-translate.service";
+
+const mockTranslations = {
+ 'en': {
+ 'chat-with-vmware-support': 'Chat with VMware Support',
+ 'return-to-chat': "Return to chat"
+ },
+ 'es': {
+ 'chat-with-vmware-support': 'Conversa con VMware Support',
+ 'return-to-chat': "Retornar a conversacion"
+ }
+};
+
+describe("VdkSimpleTranslateService", () => {
+ let service: VdkSimpleTranslateService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ VdkSimpleTranslateService
+ ]
+ });
+
+ service = TestBed.inject(VdkSimpleTranslateService);
+ });
+
+ it("should accept a LCID and set language", () => {
+ expect(service).toBeTruthy();
+ expect(service.getLanguage()).toEqual('en');
+ service.setLanguage('es-CO');
+ expect(service.getLanguage()).toEqual('es');
+ service.setLanguage('es_CO');
+ expect(service.getLanguage()).toEqual('es');
+ service.setLanguage('zh_TW');
+ expect(service.getLanguage()).toEqual('zh_TW');
+ service.setLanguage('zh');
+ expect(service.getLanguage()).toEqual('zh');
+ });
+
+ it("should translate string as per specified language", () => {
+ service.setLanguage('es-CO');
+
+ service.loadTranslationsForComponent('test', mockTranslations);
+
+ expect(service.translate('test.return-to-chat')).toEqual('Retornar a conversacion');
+ });
+
+ it("should return translation in the default langauge if the string does not exist in the specified language", () => {
+ service.setLanguage('pl-PL');
+
+ service.loadTranslationsForComponent('test', mockTranslations);
+
+ expect(service.translate('test.return-to-chat')).toEqual('Return to chat');
+ });
+
+ it("error message should show if key is not found", () => {
+ service.setLanguage('pl-PL');
+
+ service.loadTranslationsForComponent('test', mockTranslations);
+
+ expect(service.translate('test.some-non-existing-key')).toEqual('!! Key test.some-non-existing-key not found !!');
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.ts
new file mode 100644
index 0000000000..03b2047599
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/ngx-utils/simple-translate-service/simple-translate.service.ts
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from "@angular/core";
+
+const DEFAULT_LANGUAGE = 'en';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class VdkSimpleTranslateService {
+ private language: string = DEFAULT_LANGUAGE;
+ private translations: any = {};
+
+ constructor () {
+ const w: any = window as any;
+ const language = w.navigator.language || w.navigator.userLanguage;
+ this.setLanguage(language);
+ }
+
+ loadTranslationsForComponent(componentKey: string, translationsToAdd: any) {
+ for (let language in translationsToAdd) {
+ if (!this.translations[language]) {
+ this.translations[language] = {};
+ }
+
+ for (let y in translationsToAdd[language]) {
+ let newKey = componentKey + '.' + y;
+ this.translations[language][newKey] = translationsToAdd[language][y];
+ }
+ }
+ }
+
+ setLanguage(language: string) {
+ language = language.replace('-', '_');
+
+ // check if different locale names are used for Simplified or Traditional Chinese
+ const chineseMapping = {
+ 'zh_Hans': 'zh_CN',
+ 'zh_Hant': 'zh_TW'
+ }
+ language = chineseMapping[language] || language;
+
+ // Special-case Chinese because we support both TW and CN locales
+ // Also if the locale is pt_PT(Portuguese Portugal) we should default to EN
+ // otherwise the 2 character language ID is what we want
+ const mainLocale = language.substring(0, 2);
+ const isChinese = language.length > 2 && mainLocale === 'zh';
+ const isNotSupportedPortuguese = language.length > 2 && mainLocale === 'pt' && language !== 'pt_BR';
+
+ if (isChinese || isNotSupportedPortuguese) {
+ this.language = language;
+ } else {
+ this.language = mainLocale;
+ }
+ }
+
+ getLanguage() {
+ return this.language;
+ }
+
+ translate(key: any, ...args: any[]) {
+ let translations = this.translations[this.language];
+
+ //if there are no translations of the user's language set translations to equal the default language
+ if (!translations) {
+ translations = this.translations[DEFAULT_LANGUAGE] ? this.translations[DEFAULT_LANGUAGE] : [];
+ }
+
+ //if there is a translation get that translation otherwise use the default language
+ let translation = undefined;
+ if (translations[key]) {
+ translation = translations[key];
+ } else if(this.translations[DEFAULT_LANGUAGE]) {
+ translation = this.translations[DEFAULT_LANGUAGE][key];
+ }
+
+ //if no translation is found in the user's language or in the default language then show an error string
+ if (!translation) {
+ translation = `!! Key ${key} not found !!`;
+ }
+
+ return this.format(translation, ...args);
+ }
+
+ private format(format: string, ...args: string[]) {
+ return format.replace(/{(\d+)}/g, function(match, number) {
+ return typeof args[number] != 'undefined'
+ ? args[number]
+ : match;
+ });
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/public-api.ts
new file mode 100644
index 0000000000..dac97364a2
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/commons/public-api.ts
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*
+ * Public API Surface of commons
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/index.ts
new file mode 100644
index 0000000000..da2b664823
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './interfaces';
+export * from './taurus-base.component';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/index.ts
new file mode 100644
index 0000000000..9d35b0dc49
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './taurus-component-lifecycle-hooks.interface';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/taurus-component-lifecycle-hooks.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/taurus-component-lifecycle-hooks.interface.ts
new file mode 100644
index 0000000000..d7ad5c60d4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/interfaces/taurus-component-lifecycle-hooks.interface.ts
@@ -0,0 +1,139 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ComponentModel } from '../../model';
+
+/**
+ * ** Taurus Component Lifecycle hook for Model initialized.
+ *
+ *
+ */
+export interface OnTaurusModelInit {
+ /**
+ * ** Fires once per Route, after Component is initialized and
+ * immediately after {@link ComponentModel} is initialized and it is bound to the Component model field.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ */
+ onModelInit(model?: ComponentModel, task?: string): void;
+}
+
+/**
+ * @deprecated Deprecated since version 1.5.0 in favor of {@link OnTaurusModelInitialLoad}
+ *
+ * ** Taurus Component Lifecycle hook for Model First Load.
+ *
+ *
+ */
+export interface OnTaurusModelFirstLoad {
+ /**
+ * ** Fires when something in State change and its status is LOADED or FAILED, and it fires only once.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ *
+ *
+ * - General hook ideal for Ui state restore, or something that need Read->Action->Done behaviour.
+ *
+ */
+ onModelFirstLoad(model?: ComponentModel, task?: string): void;
+}
+
+
+/**
+ * ** Taurus Component Lifecycle hook for Model Initial Load.
+ *
+ *
+ */
+export interface OnTaurusModelInitialLoad {
+ /**
+ * ** Fires when something in State change and its status is LOADED or FAILED, and it fires only once.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ *
+ *
+ * - General hook ideal for Ui state restore, or something that need Read->Action->Done behaviour.
+ *
+ */
+ onModelInitialLoad(model?: ComponentModel, task?: string): void;
+}
+
+/**
+ * ** Taurus Component Lifecycle hook for Model Loaded.
+ *
+ *
+ */
+export interface OnTaurusModelLoad {
+ /**
+ * ** Fires when something in State change and its status is LOADED or FAILED.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ *
+ *
+ * - General hook ideal for loading spinner HIDE.
+ *
+ */
+ onModelLoad(model?: ComponentModel, task?: string): void;
+}
+
+/**
+ * ** Taurus Component Lifecycle hook for Model Changed.
+ *
+ *
+ */
+export interface OnTaurusModelChange {
+ /**
+ * ** Fires when something in State change and its status is LOADED.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ */
+ onModelChange(model?: ComponentModel, task?: string): void;
+}
+
+/**
+ * @deprecated Deprecated since version 1.5.0 in favor of {@link OnTaurusModelError}
+ *
+ * ** Taurus Component Lifecycle hook for Model Failed.
+ *
+ *
+ */
+export interface OnTaurusModelFail {
+ /**
+ * ** Fires when something in State change and its status is FAILED.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ */
+ onModelFail(model?: ComponentModel, task?: string): void;
+}
+
+/**
+ * ** Taurus Component Lifecycle hook for Model Failed.
+ *
+ *
+ */
+export interface OnTaurusModelError {
+ /**
+ * ** Fires when something in State change and its status is FAILED.
+ *
+ * - model - ComponentModel optional parameter.
+ * - task - Is string optional parameter that inject context to callback for the specific operation.
+ */
+ onModelError(model?: ComponentModel, task?: string): void;
+}
+
+export type TaurusComponentHooks = OnTaurusModelInit
+ & OnTaurusModelInitialLoad
+ & OnTaurusModelFirstLoad
+ & OnTaurusModelLoad
+ & OnTaurusModelChange
+ & OnTaurusModelError
+ & OnTaurusModelFail;
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.spec.ts
new file mode 100644
index 0000000000..735c3ea7ef
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.spec.ts
@@ -0,0 +1,554 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation */
+
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute, ActivatedRouteSnapshot } from '@angular/router';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { of } from 'rxjs';
+import { take } from 'rxjs/operators';
+
+import { CallFake } from '../../../unit-testing';
+
+import { TaurusObject } from '../../../common';
+
+import { RouteState } from '../../router';
+import { RouteStateFactory } from '../../router/factory';
+
+import { NavigationService } from '../../navigation';
+
+import { ComponentModel, ComponentState, FAILED, LOADED } from '../model';
+import { ComponentService } from '../services';
+
+import {
+ OnTaurusModelChange,
+ OnTaurusModelFail,
+ OnTaurusModelFirstLoad,
+ OnTaurusModelInit,
+ OnTaurusModelLoad
+} from './interfaces';
+
+import { TaurusBaseComponent } from './taurus-base.component';
+
+@Component({
+ selector: 'shared-taurus-base-subclass-component',
+ template: ''
+})
+ // eslint-disable-next-line @angular-eslint/component-class-suffix
+class TaurusBaseComponentStub extends TaurusBaseComponent
+ implements OnInit, OnDestroy,
+ OnTaurusModelInit, OnTaurusModelLoad,
+ OnTaurusModelFirstLoad, OnTaurusModelChange,
+ OnTaurusModelFail {
+
+ uuid = 'TaurusBaseComponentStub';
+
+ constructor(componentService: ComponentService,
+ navigationService: NavigationService,
+ activatedRoute: ActivatedRoute) {
+ super(componentService, navigationService, activatedRoute);
+ }
+
+ onModelInit(_model?: ComponentModel, _task?: string) {
+ // No-op.
+ }
+
+ onModelFirstLoad(_model?: ComponentModel, _task?: string) {
+ // No-op.
+ }
+
+ onModelLoad(_model?: ComponentModel, _task?: string) {
+ // No-op.
+ }
+
+ onModelChange(_model?: ComponentModel, _task?: string) {
+ // No-op.
+ }
+
+ onModelFail(_model?: ComponentModel, _task?: string) {
+ // No-op.
+ }
+
+ override ngOnInit() {
+ super.ngOnInit();
+ }
+
+ override ngOnDestroy() {
+ super.ngOnDestroy();
+ }
+}
+
+class ComponentModelStub {
+ task: string;
+
+ constructor(task?: string) {
+ this.task = task;
+ }
+
+ get status() {
+ return null;
+ }
+
+ isModified(): boolean {
+ return null;
+ }
+
+ withStatusIdle() {
+ return this;
+ }
+
+ getComponentState() {
+ return {};
+ }
+
+ getTask() {
+ return this.task;
+ }
+
+ clearTask() {
+ return this;
+ }
+}
+
+describe('TaurusBaseComponent -> TaurusSubclassComponent', () => {
+ let componentServiceStub: jasmine.SpyObj;
+ let navigationServiceStub: jasmine.SpyObj;
+ let activatedRouteStub: Partial;
+ let activatedRouteSnapshotStub: ActivatedRouteSnapshot;
+
+ let routeStateStub: RouteState;
+
+ let fixture: ComponentFixture;
+ let component: TaurusBaseComponentStub;
+ let modelStub: ComponentModel;
+
+ beforeEach(() => {
+ componentServiceStub = jasmine.createSpyObj('componentService', ['init', 'idle', 'getModel', 'update']);
+ navigationServiceStub = jasmine.createSpyObj('navigationService', ['navigateTo', 'navigateBack']);
+ activatedRouteSnapshotStub = {
+ url: '',
+ params: {},
+ data: {}
+ } as any;
+ activatedRouteStub = { snapshot: activatedRouteSnapshotStub };
+ routeStateStub = {
+ routePathSegments: ['path1/path2', 'path3/path4']
+ } as RouteState;
+ spyOn(RouteStateFactory.prototype, 'create').and.returnValue(routeStateStub);
+
+ modelStub = new ComponentModelStub() as any;
+
+ TestBed.configureTestingModule({
+ declarations: [
+ TaurusBaseComponentStub
+ ],
+ providers: [
+ { provide: ComponentService, useValue: componentServiceStub },
+ { provide: NavigationService, useValue: navigationServiceStub },
+ { provide: ActivatedRoute, useValue: activatedRouteStub }
+ ]
+ });
+
+ fixture = TestBed.createComponent(TaurusBaseComponentStub);
+ component = fixture.componentInstance;
+ });
+
+ it('should verify component is created', () => {
+ // Given
+ component.model = modelStub;
+
+ // Then
+ expect(component).toBeDefined();
+ expect(component).toBeInstanceOf(TaurusBaseComponentStub);
+ expect(component).toBeInstanceOf(TaurusBaseComponent);
+ });
+
+ describe('Properties::', () => {
+ describe('|uuid|', () => {
+ it('should verify default value is undefined', () => {
+ // Given
+ component.model = modelStub;
+
+ // When
+ expect(component.uuid).toEqual('TaurusBaseComponentStub');
+ });
+ });
+
+ describe('|componentService|', () => {
+ it('should verify ComponentService will be injected', () => {
+ // Given
+ component.model = modelStub;
+
+ // When
+ expect(component['componentService']).toBeDefined();
+ });
+ });
+
+ describe('|navigationService|', () => {
+ it('should verify NavigationService will be injected', () => {
+ // Given
+ component.model = modelStub;
+
+ // When
+ expect(component['navigationService']).toBeDefined();
+ });
+ });
+
+ describe('|activatedRoute|', () => {
+ it('should verify ActivatedRoute will be injected', () => {
+ // Given
+ component.model = modelStub;
+
+ // When
+ expect(component['activatedRoute']).toBeDefined();
+ });
+ });
+ });
+
+ describe('Angular lifecycle hooks::', () => {
+ beforeEach(() => {
+ component.model = modelStub;
+ });
+
+ describe('|ngOnInit|', () => {
+ it('should verify will invoke expected method', () => {
+ // Given
+ // @ts-ignore
+ const bindModelSpy = spyOn(component, 'bindModel').and.callFake(CallFake);
+
+ // When
+ fixture.detectChanges();
+
+ // Then
+ expect(bindModelSpy).toHaveBeenCalled();
+ });
+ });
+
+ describe('|ngOnDestroy|', () => {
+ it('should verify will invoke expected methods', () => {
+ // Given
+ spyOn(TaurusBaseComponent.prototype, 'ngOnInit').and.callFake(CallFake);
+ // @ts-ignore
+ const setIdleSpy: jasmine.Spy<() => void> = spyOn(TaurusBaseComponent.prototype, 'setStateIdle').and.callFake(CallFake);
+ const ngOnDestroySpy: jasmine.Spy<() => void> = spyOn(TaurusObject.prototype, 'ngOnDestroy').and.callFake(CallFake);
+
+ // When
+ fixture = TestBed.createComponent(TaurusBaseComponentStub);
+ fixture.componentInstance.model = modelStub;
+ fixture.detectChanges();
+ fixture.destroy();
+
+ // Then
+ expect(setIdleSpy).toHaveBeenCalled();
+ expect(ngOnDestroySpy).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('navigateTo', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const replaceValues = { '$.team': 'team1', '$.entity': 'Prime' };
+ navigationServiceStub.navigateTo.and.returnValue(Promise.resolve(true));
+ component.model = modelStub;
+
+ // When
+ const promise = component.navigateTo(replaceValues);
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(navigationServiceStub.navigateTo).toHaveBeenCalledWith(replaceValues);
+ });
+ });
+
+ describe('navigateBack', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const replaceValues = { '$.team': 'team10', '$.entity': 'Second' };
+ navigationServiceStub.navigateBack.and.returnValue(Promise.resolve(false));
+ component.model = modelStub;
+
+ // When
+ const promise = component.navigateBack(replaceValues);
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(navigationServiceStub.navigateBack).toHaveBeenCalledWith(replaceValues);
+ });
+ });
+
+ describe('|bindModel|', () => {
+ it('should verify will create correct subscriptions', () => {
+ // Given
+ componentServiceStub.init.and.returnValue(of(modelStub).pipe(take(1)));
+ componentServiceStub.getModel.and.returnValue(of(modelStub));
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(componentServiceStub.init).toHaveBeenCalledWith(component.uuid, routeStateStub);
+ expect(componentServiceStub.getModel).toHaveBeenCalledWith(component.uuid, routeStateStub.routePathSegments);
+ expect(component['subscriptions'].length).toEqual(1);
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for LOADED and modified', () => {
+ // Given
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(modelStub, 'isModified').and.returnValue(true);
+ const modelStatusSpy = spyOnProperty(modelStub, 'status', 'get').and.returnValue(LOADED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(modelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(modelStub);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).toBe(modelStub);
+ expect(modelStatusSpy).toHaveBeenCalled();
+ expect(onModelChangeSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelFailSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for LOADED and modified with Task', () => {
+ // Given
+ const task = 'delete_entity';
+ modelStub = new ComponentModelStub(task) as any;
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(modelStub, 'isModified').and.returnValue(true);
+ const modelStatusSpy = spyOnProperty(modelStub, 'status', 'get').and.returnValue(LOADED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(modelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).toBe(modelStub);
+ expect(modelStatusSpy).toHaveBeenCalled();
+ expect(onModelChangeSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelFailSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for FAILED and modified', () => {
+ // Given
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(modelStub, 'isModified').and.returnValue(true);
+ const modelStatusSpy = spyOnProperty(modelStub, 'status', 'get').and.returnValue(FAILED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(modelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(modelStub);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).toBe(modelStub);
+ expect(modelStatusSpy).toHaveBeenCalled();
+ expect(onModelFailSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for FAILED and modified with Task', () => {
+ // Given
+ const task = 'patch_entity';
+ modelStub = new ComponentModelStub(task) as any;
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(modelStub, 'isModified').and.returnValue(true);
+ const modelStatusSpy = spyOnProperty(modelStub, 'status', 'get').and.returnValue(FAILED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(modelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).toBe(modelStub);
+ expect(modelStatusSpy).toHaveBeenCalled();
+ expect(onModelFailSpy).toHaveBeenCalledWith(modelStub, task);
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for LOADED and not modified', () => {
+ // Given
+ const localModelStub: ComponentModel = new ComponentModelStub() as any;
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(localModelStub, 'isModified').and.returnValue(false);
+ const modelStatusSpy = spyOnProperty(localModelStub, 'status', 'get').and.returnValue(LOADED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(localModelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(localModelStub);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(localModelStub);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).not.toBe(localModelStub);
+ expect(modelStatusSpy).not.toHaveBeenCalled();
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
+ expect(onModelFailSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will invoke correct Taurus lifecycle hooks for LOADED and not modified with Task', () => {
+ // Given
+ const task = 'move_entity';
+ modelStub = new ComponentModelStub() as any;
+ const localModelStub: ComponentModel = new ComponentModelStub(task) as any;
+ const onModelInitSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelInit').and.callFake(CallFake);
+ const onModelFirstLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFirstLoad').and.callFake(CallFake);
+ const onModelLoadSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelLoad').and.callFake(CallFake);
+ const onModelChangeSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelChange').and.callFake(CallFake);
+ const onModelFailSpy = spyOn(TaurusBaseComponentStub.prototype, 'onModelFail').and.callFake(CallFake);
+ const isModelModifiedSpy = spyOn(localModelStub, 'isModified').and.returnValue(false);
+ const modelStatusSpy = spyOnProperty(localModelStub, 'status', 'get').and.returnValue(LOADED);
+
+ componentServiceStub.init.and.returnValue(of(modelStub));
+ componentServiceStub.getModel.and.returnValue(of(localModelStub));
+
+ // Then 1
+ expect(component.model).toBeUndefined();
+
+ // When
+ // @ts-ignore
+ component.bindModel();
+
+ // Then 2
+ expect(component.model).toBe(modelStub);
+ expect(onModelInitSpy).toHaveBeenCalledWith(modelStub);
+ expect(onModelFirstLoadSpy).toHaveBeenCalledWith(localModelStub, task);
+ expect(onModelLoadSpy).toHaveBeenCalledWith(localModelStub, task);
+ expect(isModelModifiedSpy).toHaveBeenCalledWith(modelStub);
+ expect(component.model).not.toBe(localModelStub);
+ expect(modelStatusSpy).not.toHaveBeenCalled();
+ expect(onModelChangeSpy).not.toHaveBeenCalled();
+ expect(onModelFailSpy).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('|setStateIdle|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const componentState: ComponentState = {
+ id: 'TaurusBaseComponentStub',
+ status: LOADED,
+ toLiteral: CallFake,
+ toLiteralDeepClone: CallFake,
+ copy: CallFake
+ };
+ const setStatusIdleSpy = spyOn(modelStub, 'withStatusIdle').and.callFake(() => modelStub);
+ const getComponentStateSpy = spyOn(modelStub, 'getComponentState').and.returnValue(componentState);
+ component.model = modelStub;
+
+ // When
+ // @ts-ignore
+ component.setStateIdle();
+
+ // Then
+ expect(setStatusIdleSpy).toHaveBeenCalled();
+ expect(getComponentStateSpy).toHaveBeenCalled();
+ expect(componentServiceStub.idle).toHaveBeenCalledWith(componentState);
+ });
+ });
+
+ describe('|normalizeModelState|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const componentState: ComponentState = {
+ id: 'TaurusBaseComponentStub',
+ status: LOADED,
+ toLiteral: CallFake,
+ toLiteralDeepClone: CallFake,
+ copy: CallFake
+ };
+ const clearTaskSpy = spyOn(modelStub, 'clearTask').and.callFake(() => modelStub);
+ const getComponentStateSpy = spyOn(modelStub, 'getComponentState').and.returnValue(componentState);
+ component.model = modelStub;
+
+ // When
+ // @ts-ignore
+ component.normalizeModelState(modelStub);
+
+ // Then
+ expect(clearTaskSpy).toHaveBeenCalled();
+ expect(getComponentStateSpy).toHaveBeenCalled();
+ expect(componentServiceStub.update).toHaveBeenCalledWith(componentState);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.ts
new file mode 100644
index 0000000000..f92668aa98
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/base/taurus-base.component.ts
@@ -0,0 +1,335 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @angular-eslint/directive-class-suffix */
+
+import { Directive, OnDestroy, OnInit } from '@angular/core';
+import { ActivatedRoute, Params } from '@angular/router';
+
+import { Subscription } from 'rxjs';
+
+import { ArrayElement, CollectionsUtil } from '../../../utils';
+
+import { TaurusNavigateAction, TaurusObject } from '../../../common';
+
+import { NavigationService } from '../../navigation';
+import { RouteStateFactory } from '../../router/factory';
+
+import { ComponentModel, FAILED } from '../model';
+import { ComponentService } from '../services';
+
+import { TaurusComponentHooks } from './interfaces';
+
+/**
+ * ** Superclass Component for all other Components that want to use NgRx Store and all lifecycle hooks from Taurus.
+ *
+ *
+ */
+@Directive()
+export abstract class TaurusBaseComponent extends TaurusObject implements OnInit, OnDestroy {
+ private static _routeStateFactory: RouteStateFactory;
+
+ /**
+ * ** Field that hold Component Model.
+ */
+ model: ComponentModel;
+
+ /**
+ * ** UUID is identifier for every Subclass in Components state Store.
+ */
+ abstract readonly uuid: string;
+
+ /**
+ * ** Feature flag to enforce Route reuse in native way provided from Taurus NgRx.
+ *
+ * - Introduced for backward compatibility.
+ * - Default value is false, and continues to operate like previous major version.
+ * - If set to true will enforce Route reuse strategy and will re-initialize Component with new Model for the new params.
+ */
+ enforceRouteReuse = false;
+
+ /**
+ * ** Model subscription ref.
+ * @private
+ */
+ private _modelSubscription: Subscription;
+
+ /**
+ * ** Constructor.
+ */
+ protected constructor(protected readonly componentService: ComponentService,
+ protected readonly navigationService: NavigationService,
+ protected readonly activatedRoute: ActivatedRoute) {
+ super();
+ }
+
+ /**
+ * ** Navigate to page using {@link NavigationService.navigateTo}.
+ */
+ navigateTo(replaceValues?: { [key: string]: ArrayElement['replaceValue'] }): Promise {
+ return this
+ .navigationService
+ .navigateTo(replaceValues);
+ }
+
+ /**
+ * ** Navigate back to previous page using {@link NavigationService.navigateBack}.
+ */
+ navigateBack(replaceValues?: { [key: string]: ArrayElement['replaceValue'] }): Promise {
+ return this
+ .navigationService
+ .navigateBack(replaceValues);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ ngOnInit() {
+ this.bindModel();
+
+ this.initializeRouteReuse();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ override ngOnDestroy() {
+ this.setStateIdle();
+
+ super.ngOnDestroy();
+ }
+
+ /**
+ * ** Invoking method register subscriber for Taurus NgRx Redux Store mutation in context of {@link ComponentState.routePathSegments},
+ * which binds {@link ComponentModel} to {@link TaurusBaseComponent.model}
+ * and start invocation of Taurus NgRx Redux Component lifecycle hooks.
+ *
+ * Invocation order:
+ *
+ * 1. {@link OnTaurusModelInit}
+ * 2. {@link OnTaurusModelInitialLoad} or {@link OnTaurusModelFirstLoad} - only one could be invoke,
+ * where deprecated shouldn't be implemented anymore.
+ * 3. {@link OnTaurusModelLoad}
+ * 4. {@link OnTaurusModelChange} when status is {@link LOADED}
+ * 5. {@link OnTaurusModelError} or {@link OnTaurusModelFail} when status is {@link FAILED} - only one could be invoke,
+ * where deprecated shouldn't be implemented anymore.
+ *
+ *
+ *
+ * Override it if you want to change default behavior.
+ *
+ *
+ * @protected
+ */
+ protected bindModel(): void {
+ let isOnModelInitialLoadExecuted = false;
+
+ if (!TaurusBaseComponent._routeStateFactory) {
+ TaurusBaseComponent._routeStateFactory = new RouteStateFactory();
+ }
+
+ const routeState = TaurusBaseComponent
+ ._routeStateFactory
+ .create(this.activatedRoute.snapshot, null);
+
+ this.componentService
+ .init(this.uuid, routeState)
+ .subscribe((model) => {
+ this.model = model;
+
+ TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelInit', model);
+ });
+
+ this._modelSubscription = this.componentService
+ .getModel(this.uuid, routeState.routePathSegments)
+ .subscribe((model) => {
+ if (!isOnModelInitialLoadExecuted) {
+ isOnModelInitialLoadExecuted = true;
+ if (!TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelInitialLoad', model)) {
+ TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelFirstLoad', model);
+ }
+ }
+
+ this.evaluateTaurusComponentLifecycleHooks(model);
+ });
+
+ this.subscriptions.push(this._modelSubscription);
+ }
+
+ /**
+ * ** Evaluates Taurus NgRx Redux Component lifecycle hooks
+ * ({@link OnTaurusModelLoad} and {@link OnTaurusModelChange} and ({@link OnTaurusModelFail} or {@link OnTaurusModelError})).
+ *
+ * - Override it if you want to change default behavior.
+ *
+ * @protected
+ */
+ protected evaluateTaurusComponentLifecycleHooks(model: ComponentModel): void {
+ TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelLoad', model);
+
+ if (!this.isModelModified(model)) {
+ return;
+ }
+
+ this.refreshModel(model);
+
+ if (model.status === FAILED) {
+ if (!TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelError', model)) {
+ TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelFail', model);
+ }
+ } else {
+ TaurusBaseComponent._executeTaurusComponentHook(this, 'onModelChange', model);
+ }
+
+ try {
+ this.normalizeModelState(model);
+ } catch (e) {
+ console.error(`Taurus NgRx Redux failed to normalize ComponentModel!`, e);
+ }
+ }
+
+ /**
+ * ** Refresh model field {@link TaurusBaseComponent.model} with new one, assignment by reference.
+ *
+ * - Override it if you want to change default behavior.
+ *
+ * @protected
+ */
+ protected refreshModel(model: ComponentModel): void {
+ this.model = model;
+ }
+
+ /**
+ * ** Normalize Model state, by default it clear Task field in {@link ComponentState.task} and update model in Taurus NgRx Redux Store.
+ *
+ * - It is invoked only if {@link ComponentModel} is modified and after invocation of all Taurus components lifecycle hooks.
+ * - Override it if you want to change default behavior.
+ *
+ * @protected
+ */
+ protected normalizeModelState(model: ComponentModel): void {
+ this.componentService.update(
+ model
+ .clearTask()
+ .getComponentState()
+ );
+ }
+
+ /**
+ * ** Set Model state in IDLE to stop listening on Events from Store.
+ *
+ * - It is invoked by default before Component is destroyed, in Angular lifecycle hook {@link OnDestroy}.
+ * - Override it if you want to change default behavior.
+ *
+ * @protected
+ */
+ protected setStateIdle(): void {
+ this.componentService.idle(
+ this.model
+ .withStatusIdle()
+ .getComponentState()
+ );
+ }
+
+ /**
+ * ** Evaluation of this method acknowledge that new {@link ComponentModel} is modified or not.
+ *
+ * - Comparison is evaluated between provided Model and assigned Component's Model {@link TaurusBaseComponent.model}.
+ * - Default implementation use {@link ComponentModel.isModified} for deep Comparison of specific fields.
+ * - Override it if you want to change default behavior.
+ *
+ *
+ * Be cautious about your changes and intents!
+ * Examples what can wrong comparison do?
+ *
+ *
+ * 1. Infinite lifecycle hooks invocation, where consequences are: performance deterioration or application freeze.
+ * 2. Prevent lifecycle hooks invocation, where consequences are: your Data never arrive to your Component fields.
+ *
+ * @protected
+ */
+ protected isModelModified(model: ComponentModel): boolean {
+ return model.isModified(this.model);
+ }
+
+ /**
+ * ** Initialize Route reuse strategy for Component in context of Taurus NgRx.
+ * - Turns on listener for Activated params change and if detects mutation
+ * sets current model in current RoutePathSegment to idle,
+ * and bind for new model stream to the new RoutePathSegment.
+ * - New feature this is completely backward compatible,
+ * and it can be turned on with feature flag per Component Class.
+ *
+ * @protected
+ */
+ protected initializeRouteReuse(): void {
+ if (!this.enforceRouteReuse) {
+ return;
+ }
+
+ let previousParams: Params;
+
+ this.subscriptions.push(
+ this.activatedRoute
+ .params
+ .subscribe((params) => {
+ if (CollectionsUtil.isNil(previousParams)) {
+ previousParams = params;
+
+ return;
+ }
+
+ if (!CollectionsUtil.isEqual(previousParams, params)) {
+ previousParams = params;
+
+ const isRemoveSuccessful = this.removeSubscriptionRef(this._modelSubscription);
+ if (isRemoveSuccessful) {
+ // set current RoutePathSegment model state to idle
+ this.setStateIdle();
+ // bind new model stream to new RoutePathSegment
+ this.bindModel();
+ }
+ }
+ })
+ );
+ }
+
+ /**
+ * ** Invoke Taurus NgRx Redux Component lifecycle hook.
+ *
+ * - Lifecycle hooks are invoked only if implementation they are found as implemented in subclasses.
+ * - Returns true if method is found and executed, otherwise false.
+ *
+ * @private
+ */
+ // eslint-disable-next-line @typescript-eslint/member-ordering
+ private static _executeTaurusComponentHook(
+ instance: TaurusBaseComponent,
+ method: keyof TaurusComponentHooks,
+ model: ComponentModel): boolean {
+
+ if (CollectionsUtil.isFunction(instance[method])) {
+ const currentTask = model.getTask();
+
+ try {
+ if (CollectionsUtil.isString(currentTask)) {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ instance[method](model, currentTask);
+ } else {
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call
+ instance[method](model);
+ }
+ } catch (e) {
+ console.error(`Taurus NgRx Redux failed to execute lifecycle hook "${ method }"!`, e);
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/index.ts
new file mode 100644
index 0000000000..ec2d13fdc8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/index.ts
@@ -0,0 +1,11 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './base';
+export * from './model';
+export * from './services';
+export * from './state';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.spec.ts
new file mode 100644
index 0000000000..22092047f4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.spec.ts
@@ -0,0 +1,235 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../../utils';
+
+import { ComparableImpl } from '../../../common';
+
+import { RouterState, RouteSegments, RouteState } from '../../router';
+
+import { ComponentState, ComponentStateImpl, IDLE, LOADED } from './state';
+
+import { ComponentModel } from './component.model';
+
+import { ComponentModelComparable } from './component-model.comparable';
+
+describe('ComponentModelComparable', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new ComponentModelComparable(null);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentModelComparable.of(null);
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentModelComparable);
+ expect(instance).toBeInstanceOf(ComparableImpl);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ let componentState: ComponentState;
+ let routerState: RouterState;
+ let modelComparable: ComponentModelComparable;
+
+ beforeEach(() => {
+ componentState = ComponentStateImpl.of({
+ id: 'testId',
+ status: IDLE,
+ data: new Map([['countries', ['aCountry', 'bCountry', 'cCountry']]])
+ });
+ routerState = RouterState.of(
+ RouteState.of(
+ RouteSegments.of(
+ 'entity/117',
+ {},
+ { entityId: '107' },
+ { search: 'test-team-107' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'test-team-107' }
+ )
+ ),
+ 'domain/context/entity/117'
+ ),
+ 43
+ );
+ modelComparable = ComponentModelComparable.of(ComponentModel.of(componentState, routerState));
+ });
+
+ describe('|compare|', () => {
+ it('should verify will return -1 when stored comparable value is not instance of ComponentModel', () => {
+ // Given
+ modelComparable = ComponentModelComparable.of(null);
+
+ // When
+ const compareV = modelComparable.compare(ComponentModelComparable.of(undefined));
+
+ // Then
+ expect(compareV).toEqual(-1);
+ });
+
+ it('should verify will return -1 when provided comparable is not instance of expected', () => {
+ // When
+ const compareV = modelComparable.compare(ComponentModelComparable.of(null));
+
+ // Then
+ expect(compareV).toEqual(-1);
+ });
+
+ it('should verify will return -1 when no comparable model provided', () => {
+ // When
+ const compareV = modelComparable.compare(null);
+
+ // Then
+ expect(compareV).toEqual(-1);
+ });
+
+ it('should verify will return 0 when ComponentModel ref is same', () => {
+ // Given
+ const isEqualSpy = spyOn(CollectionsUtil, 'isEqual').and.callThrough();
+
+ // When
+ modelComparable.value.withPage(10, 100);
+ modelComparable.value.getComponentState().uiState.set('submitBtn', { state: 'submitted' });
+ const compareV = modelComparable.compare(modelComparable);
+
+ // Then
+ expect(compareV).toEqual(0);
+ expect(isEqualSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will return -1 when status is different', () => {
+ // Given
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ status: LOADED
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(-1);
+ });
+
+ it('should verify will return -1 when task is different', () => {
+ // Given
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ task: 'patch_entity'
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(-1);
+ });
+
+ it('should verify will return 0 when navigationId is different', () => {
+ // Given
+ const routerStateComparable = RouterState.of(
+ routerState.state,
+ 90
+ );
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentState, routerStateComparable));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(0);
+ });
+
+ it('should verify will return -1 when error is different', () => {
+ // Given
+ const isEqualSpy = spyOn(CollectionsUtil, 'isEqual').and.callThrough();
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ error: new Error('new Error()')
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(-1);
+ expect(isEqualSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will return 0 when data ref is same', () => {
+ // Given
+ const isEqualSpy = spyOn(CollectionsUtil, 'isEqual').and.callThrough();
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ data: componentState.data
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(0);
+ expect(isEqualSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will return 0 when ref is different but content is same', () => {
+ // Given
+ const areMapsEqualSpy = spyOn(CollectionsUtil, 'areMapsEqual').and.callThrough();
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ data: new Map([['countries', ['aCountry', 'bCountry', 'cCountry']]])
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(0);
+ expect(areMapsEqualSpy).toHaveBeenCalledWith(componentState.data, componentStateComparable.data);
+ });
+
+ it('should verify will return -1 when ref is different and content is different', () => {
+ // Given
+ const areMapsEqualSpy = spyOn(CollectionsUtil, 'areMapsEqual').and.callThrough();
+ const componentStateComparable = ComponentStateImpl.of({
+ ...componentState,
+ data: new Map([
+ ['countries', ['aCountry', 'bCountry', 'cCountry']],
+ ['users', ['aUser', 'bUser', 'cUser']]
+ ])
+ });
+ const modelComparableDI = ComponentModelComparable.of(ComponentModel.of(componentStateComparable, routerState));
+
+ // When
+ const compareV = modelComparable.compare(modelComparableDI);
+
+ // Then
+ expect(compareV).toEqual(-1);
+ expect(areMapsEqualSpy).toHaveBeenCalledWith(componentState.data, componentStateComparable.data);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.ts
new file mode 100644
index 0000000000..9a6c427de7
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component-model.comparable.ts
@@ -0,0 +1,75 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../../utils';
+
+import { Comparable, ComparableImpl } from '../../../common';
+
+import { AbstractComponentModel } from './component.model.interface';
+
+export class ComponentModelComparable extends ComparableImpl {
+ /**
+ * ** Constructor.
+ */
+ constructor(model: AbstractComponentModel) {
+ super(model);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(model: AbstractComponentModel): ComponentModelComparable {
+ return new ComponentModelComparable(model);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ override compare(comparable: Comparable): number {
+ if (comparable instanceof ComponentModelComparable) {
+ return this._compareEquality(comparable);
+ } else {
+ return -1;
+ }
+ }
+
+ private _compareEquality(comparable: ComponentModelComparable): number {
+ if (!(this.value instanceof AbstractComponentModel)) {
+ return -1;
+ }
+
+ if (!(comparable.value instanceof AbstractComponentModel)) {
+ return -1;
+ }
+
+ if (this.value === comparable.value) {
+ return 0;
+ }
+
+ if (this.value.status !== comparable.value.status) {
+ return -1;
+ }
+
+ if (this.value.getTask() !== comparable.value.getTask()) {
+ return -1;
+ }
+
+ if (this.value.getComponentState().error !== comparable.value.getComponentState().error) {
+ return -1;
+ }
+
+ if (this.value.getComponentState().data === comparable.value.getComponentState().data) {
+ return 0;
+ }
+
+ if (CollectionsUtil.areMapsEqual(this.value.getComponentState().data, comparable.value.getComponentState().data)) {
+ return 0;
+ }
+
+ return -1;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.interface.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.interface.ts
new file mode 100644
index 0000000000..1c46014743
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.interface.ts
@@ -0,0 +1,186 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { ApiPredicate } from '../../../common';
+
+import { RouterState } from '../../router';
+
+import { ComponentState, StatusType } from './state';
+
+/**
+ * ** Interface for Model for all Components.
+ *
+ *
+ */
+export abstract class AbstractComponentModel {
+
+ /**
+ * ** Return RouterState.
+ */
+ abstract get routerState(): RouterState;
+
+ /**
+ * ** Return Status from ComponentState.
+ */
+ abstract get status(): StatusType;
+
+ /**
+ * ** Return routePath from RouterState.
+ */
+ abstract get routePath(): string;
+
+ /**
+ * ** Return the ComponentState.
+ */
+ abstract getComponentState(): ComponentState;
+
+ /**
+ * ** Set Search query to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withSearch(search: string): this;
+
+ /**
+ * ** Set Page to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withPage(page: number, size: number): this;
+
+ /**
+ * ** Set Filter to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withFilter(filterPredicates: ApiPredicate[]): this;
+
+ /**
+ * ** Set Request params to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withRequestParam(key: string, value: any): this;
+
+ /**
+ * ** Set Data (bound to key identifier) to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withData(key: string, data: any): this;
+
+ /**
+ * ** Set Task identifier to ComponentState and get Model reference again.
+ *
+ * - Should be set only after data comes from the API, action is asynchronous.
+ * - Don't set this property during action dispatch
+ *
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withTask(taskIdentifier: string): this;
+
+ /**
+ * ** Clear Task identifier from ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract clearTask(): this;
+
+ /**
+ * ** Returns latest Task from ComponentState.
+ */
+ abstract getTask(): string;
+
+ /**
+ * ** Returns latest Task unique identifier from ComponentState.
+ */
+ abstract getTaskUniqueIdentifier(): string;
+
+ /**
+ * ** Set Error to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withError(error: Error): this;
+
+ /**
+ * ** Clear Error from ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract clearError(): this;
+
+ /**
+ * ** Set UiState for given identifier to ComponentState and get Model reference again.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withUiState(key: string, value: any): this;
+
+ /**
+ * ** Returns UiState for given identifier.
+ */
+ abstract getUiState(key: string): T;
+
+ /**
+ * ** Set Component State status to IDLE.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withStatusIdle(): this;
+
+ /**
+ * ** Set Component State status to LOADING.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withStatusLoading(): this;
+
+ /**
+ * ** Set Component State status to LOADED.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withStatusLoaded(): this;
+
+ /**
+ * ** Set Component State status to FAILED.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract withStatusFailed(): this;
+
+ /**
+ * ** Update Component State with Partial Component State patch.
+ *
+ * - Ready for method chaining.
+ *
+ */
+ abstract updateComponentState(patchState: Partial): this;
+
+ /**
+ * ** Filter method that analyze if current Model is different from given one.
+ */
+ abstract isModified(model: AbstractComponentModel): boolean;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.spec.ts
new file mode 100644
index 0000000000..557d4cab14
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.spec.ts
@@ -0,0 +1,449 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ASC, RequestFilterImpl, RequestPageImpl } from '../../../common';
+
+import { RouterState, RouteSegments, RouteState } from '../../router';
+
+import { ComponentState, ComponentStateImpl, FAILED, IDLE, INITIALIZED, LOADED, LOADING } from './state';
+
+import { ComponentModel } from './component.model';
+
+import { ComponentModelComparable } from './component-model.comparable';
+
+describe('ComponentModel', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new ComponentModel(null, null);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::()', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // Given
+ const componentState = { id: 'testId', status: IDLE } as ComponentState;
+ const routerState = RouterState.of(
+ {
+ routeSegments: {
+ routePath: 'domain/context'
+ }
+ } as RouteState,
+ 9
+ );
+
+ // When
+ const instance = ComponentModel.of(componentState, routerState);
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentModel);
+ });
+ });
+ });
+ });
+
+ describe('Getters/Setters::', () => {
+ let componentState: ComponentState;
+ let routerState: RouterState;
+
+ beforeEach(() => {
+ componentState = { id: 'testId', status: IDLE } as ComponentState;
+ routerState = RouterState.of(
+ {
+ routeSegments: {
+ routePath: 'domain/context'
+ }
+ } as RouteState,
+ 9
+ );
+ });
+
+ describe('|GET -> routerState|', () => {
+ it('should verify will return correct value', () => {
+ // Given
+ const model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const _routerState = model.routerState;
+
+ // Then
+ expect(_routerState).toBe(routerState);
+ });
+ });
+
+ describe('|GET -> status|', () => {
+ it('should verify will return correct value', () => {
+ // Given
+ const model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const status = model.status;
+
+ // Then
+ expect(status).toEqual(IDLE);
+ });
+ });
+
+ describe('|GET -> routePath|', () => {
+ it('should verify will return correct value from componentState', () => {
+ // Given
+ componentState = { ...componentState, routePath: 'entity/15' };
+ const model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const routePath = model.routePath;
+
+ // Then
+ expect(routePath).toEqual('entity/15');
+ });
+
+ it('should verify will return correct value from routerState', () => {
+ // Given
+ const model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const routePath = model.routePath;
+
+ // Then
+ expect(routePath).toEqual('domain/context');
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ let componentState: ComponentState;
+ let routerState: RouterState;
+ let model: ComponentModel;
+
+ beforeEach(() => {
+ componentState = ComponentStateImpl.of({
+ id: 'testId',
+ status: IDLE,
+ data: new Map([['countries', ['aCountry', 'bCountry', 'cCountry']]])
+ });
+ routerState = RouterState.of(
+ RouteState.of(
+ RouteSegments.of(
+ 'entity/21',
+ { entityId: '21' },
+ { search: 'test-team' },
+ RouteSegments.of('domain/context',
+ {},
+ { search: 'test-team' }
+ )
+ ),
+ 'domain/context/entity/21'
+ ),
+ 9
+ );
+ model = ComponentModel.of(componentState, routerState);
+ });
+
+ describe('|getComponentState|', () => {
+ it('should verify will return ComponentState', () => {
+ // When
+ const _componentState = model.getComponentState();
+
+ // Then
+ expect(_componentState).toBe(componentState);
+ });
+ });
+
+ describe('|withSearch|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withSearch('metadataSearchParam');
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ search: 'metadataSearchParam' });
+ });
+ });
+
+ describe('|withPage|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withPage(6, 18);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ page: RequestPageImpl.of(6, 18) });
+ });
+ });
+
+ describe('|withFilter|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withFilter([{ pattern: 'test*', property: 'config.path', sort: ASC }]);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({
+ filter: RequestFilterImpl.of({
+ pattern: 'test*',
+ property: 'config.path',
+ sort: ASC
+ })
+ });
+ });
+ });
+
+ describe('|withRequestParam|', () => {
+ it('should verify will set data to Request param map', () => {
+ // Given
+ const attributes = ['topBrand', 'extraSize', 'largeNumber'];
+ const order = { sort: ASC, path: 'config.user' };
+
+ // When
+ const ref = model
+ .withRequestParam('attributes', attributes)
+ .withRequestParam('order', order);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(model.getComponentState().requestParams).toEqual(
+ new Map([['attributes', [...attributes]], ['order', { ...order }]])
+ );
+ });
+ });
+
+ describe('|withData|', () => {
+ it('should verify will set data to Data map', () => {
+ // Given
+ const users = ['aUser', 'bUser', 'cUser'];
+
+ // When
+ const ref = model.withData('users', users);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(model.getComponentState().data).toEqual(
+ new Map([['countries', ['aCountry', 'bCountry', 'cCountry']], ['users', users]])
+ );
+ });
+ });
+
+ describe('|withTask|', () => {
+ it('should verify will set Task to ComponentState', () => {
+ // Given
+ const task = 'delete_entity';
+
+ // Then 1
+ expect(model.getComponentState().task).not.toEqual(task);
+
+ // When
+ const ref = model.withTask(task);
+
+ // Then 2
+ expect(ref).toBe(model);
+ expect(model.getComponentState().task).toEqual(task);
+ });
+ });
+
+ describe('|getTask|', () => {
+ it('should verify will return Task from ComponentState', () => {
+ // Given
+ const task = 'search_users';
+ const taskIdentifier = `${ task } __ ${ new Date().toISOString() }`;
+ componentState = ComponentStateImpl.of({
+ ...componentState,
+ task: taskIdentifier
+ });
+ model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const res = model.getTask();
+
+ // Then
+ expect(res).toBe(task);
+ });
+ });
+
+ describe('|withError|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const error = new Error('random error');
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withError(error);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ error });
+ });
+ });
+
+ describe('|clearError|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.clearError();
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ error: null });
+ });
+ });
+
+ describe('|withUiState|', () => {
+ it('should verify will set data to Data map', () => {
+ // Given
+ const btnState = { active: true };
+
+ // When
+ const ref = model.withUiState('btnOk', btnState);
+
+ // Then
+ expect(ref).toBe(model);
+ expect(model.getComponentState().uiState).toEqual(new Map([['btnOk', btnState]]));
+ });
+ });
+
+ describe('|getUiState|', () => {
+ it('should verify will return uiState from ComponentState for given key', () => {
+ // Given
+ const btnState = { active: true };
+ componentState = ComponentStateImpl.of({
+ ...componentState,
+ uiState: new Map([['btnOk', btnState]])
+ });
+ model = ComponentModel.of(componentState, routerState);
+
+ // When
+ const uiState = model.getUiState('btnOk');
+
+ // Then
+ expect(uiState).toBe(btnState);
+ });
+ });
+
+ describe('|withStatusIdle|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withStatusIdle();
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ status: IDLE });
+ });
+ });
+
+ describe('|withStatusLoading|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withStatusLoading();
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ status: LOADING });
+ });
+ });
+
+ describe('|withStatusLoaded|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withStatusLoaded();
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ status: LOADED });
+ });
+ });
+
+ describe('|withStatusFailed|', () => {
+ it('should verify will invoke correct method with correct payload', () => {
+ // Given
+ const updateSpy = spyOn(model, 'updateComponentState').and.callThrough();
+
+ // When
+ const ref = model.withStatusFailed();
+
+ // Then
+ expect(ref).toBe(model);
+ expect(updateSpy).toHaveBeenCalledWith({ status: FAILED });
+ });
+ });
+
+ describe('|updateComponentState|', () => {
+ it('should verify will update local ComponentState', () => {
+ // Given
+ const assertionComponentState = ComponentStateImpl.of({
+ ...componentState,
+ status: INITIALIZED,
+ id: 'test-component-1234',
+ search: 'teamUSA*',
+ page: RequestPageImpl.of(9, 45)
+ });
+
+ // When
+ const ref = model.updateComponentState({
+ status: INITIALIZED,
+ id: 'test-component-1234',
+ search: 'teamUSA*',
+ page: RequestPageImpl.of(9, 45)
+ });
+
+ // Then
+ expect(ref).toBe(model);
+ expect(model.getComponentState()).toEqual(assertionComponentState);
+ });
+ });
+
+ describe('|isModified|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const componentModelComparableStub = jasmine.createSpyObj('comparable', ['notEqual']);
+ componentModelComparableStub.notEqual.and.returnValue(false);
+ const factoryOfSpy = spyOn(ComponentModelComparable, 'of').and.returnValue(componentModelComparableStub);
+
+ const comparableState = ComponentStateImpl.of({
+ ...componentState,
+ data: new Map([
+ ['countries', ['aCountry', 'bCountry', 'cCountry']],
+ ['users', ['aUser', 'bUser', 'cUser']]
+ ])
+ });
+ const comparableModel = ComponentModel.of(comparableState, routerState);
+
+ // When
+ const isModified = model.isModified(comparableModel);
+
+ // Then
+ expect(isModified).toBeFalse();
+ expect(factoryOfSpy).toHaveBeenCalledTimes(2);
+ expect(factoryOfSpy.calls.argsFor(0)).toEqual([model]);
+ expect(factoryOfSpy.calls.argsFor(1)).toEqual([comparableModel]);
+ expect(componentModelComparableStub.notEqual).toHaveBeenCalledTimes(1);
+ expect(componentModelComparableStub.notEqual).toHaveBeenCalledWith(componentModelComparableStub);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.ts
new file mode 100644
index 0000000000..45c3163191
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/component.model.ts
@@ -0,0 +1,250 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { ApiPredicate, extractTaskFromIdentifier, RequestFilterImpl, RequestPageImpl } from '../../../common';
+
+import { RouterState } from '../../router';
+
+import { ComponentState, ComponentStateImpl, FAILED, IDLE, LOADED, LOADING, StatusType } from './state';
+
+import { ComponentModelComparable } from './component-model.comparable';
+
+import { AbstractComponentModel } from './component.model.interface';
+
+/**
+ * ** Generic Model for all Components.
+ *
+ *
+ */
+export class ComponentModel extends AbstractComponentModel {
+ /**
+ * ** Constructor.
+ */
+ constructor(protected _componentState: ComponentState,
+ protected _routerState: RouterState) {
+ super();
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(componentState: ComponentState, routerState: RouterState) {
+ return new ComponentModel(componentState, routerState);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ get routerState(): RouterState {
+ return this._routerState;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ get status(): StatusType {
+ return this.getComponentState().status;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ get routePath(): string {
+ return this.getComponentState().routePath ||
+ this.routerState.state.routeSegments.routePath;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getComponentState(): ComponentState {
+ return this._componentState;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withSearch(search: string) {
+ this.updateComponentState({
+ search
+ });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withPage(page: number, size: number) {
+ this.updateComponentState({
+ page: RequestPageImpl.of(page, size)
+ });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withFilter(filterPredicates: ApiPredicate[]) {
+ this.updateComponentState({
+ filter: RequestFilterImpl.of(...filterPredicates)
+ });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withRequestParam(key: string, value: any) {
+ this.getComponentState()
+ .requestParams
+ .set(key, value);
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withData(key: string, data: any) {
+ this.getComponentState()
+ .data
+ .set(key, data);
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withTask(taskIdentifier: string) {
+ this.updateComponentState({ task: taskIdentifier });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ clearTask() {
+ this.updateComponentState({ task: null });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getTask(): string {
+ return extractTaskFromIdentifier(this.getComponentState().task);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getTaskUniqueIdentifier(): string {
+ return this.getComponentState().task;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withError(error: Error) {
+ this.updateComponentState({ error });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ clearError() {
+ this.updateComponentState({ error: null });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withUiState(key: string, value: any) {
+ this.getComponentState()
+ .uiState
+ .set(key, value);
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getUiState(key: string): T {
+ return this.getComponentState()
+ .uiState
+ .get(key) as T;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withStatusIdle() {
+ this.updateComponentState({ status: IDLE });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withStatusLoading() {
+ this.updateComponentState({ status: LOADING });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withStatusLoaded() {
+ this.updateComponentState({ status: LOADED });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ withStatusFailed() {
+ this.updateComponentState({ status: FAILED });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ updateComponentState(patchState: Partial) {
+ this._componentState = ComponentStateImpl.of({
+ ...this.getComponentState(),
+ ...patchState
+ });
+
+ return this;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ isModified(model: ComponentModel): boolean {
+ return ComponentModelComparable.of(this)
+ .notEqual(ComponentModelComparable.of(model));
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/index.ts
new file mode 100644
index 0000000000..9409d7dea5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './state';
+export * from './component.model.interface';
+export * from './component.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.spec.ts
new file mode 100644
index 0000000000..2143716c84
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.spec.ts
@@ -0,0 +1,275 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ApiPredicate, ASC, RequestFilterImpl, RequestOrderImpl, RequestPageImpl } from '../../../../common';
+
+import { IDLE, LOADED } from './component-status.model';
+
+import { ComponentState, ComponentStateImpl, LiteralComponentState } from './component-state.model';
+
+describe('ComponentStateImpl', () => {
+ let partialStateMock: Partial;
+ let stateMock: ComponentStateImpl;
+ let literalStateMock: LiteralComponentState;
+
+ beforeEach(() => {
+ const apiPredicate1: ApiPredicate = { sort: ASC, property: 'test.property.10', pattern: 'test.pattern.20' };
+ const apiPredicate2: ApiPredicate = { sort: ASC, property: 'test.property.30', pattern: 'test.pattern.40' };
+
+ const uiState1 = { order: 'ASC' };
+ const uiState2 = [1, 2, 3];
+
+ const data1 = { data: { name: 'aName' } };
+ const data2 = { content: { page: { size: 10, page: 2 } } };
+
+ const dateNowISO = new Date().toISOString();
+
+ partialStateMock = {
+ id: 'testId',
+ status: LOADED,
+ navigationId: 11,
+ error: null,
+ search: 'testSearch',
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['domain/context', 'entity/10'],
+ page: new RequestPageImpl(5, 10),
+ order: new RequestOrderImpl(apiPredicate1),
+ filter: new RequestFilterImpl(apiPredicate2),
+ requestParams: new Map([
+ ['test_param.1', apiPredicate1],
+ ['test_param.2', apiPredicate2]
+ ]),
+ task: `delete_user __ ${ dateNowISO }`,
+ uiState: new Map([
+ ['test_uiState.1', uiState1],
+ ['test_uiState.2', uiState2]
+ ]),
+ data: new Map([
+ ['test_data.1', data1],
+ ['test_data.2', data2]
+ ])
+ };
+
+ stateMock = new ComponentStateImpl(partialStateMock);
+
+ literalStateMock = {
+ id: 'testId',
+ status: LOADED,
+ navigationId: 11,
+ error: null,
+ search: 'testSearch',
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['domain/context', 'entity/10'],
+ page: { pageNumber: 5, pageSize: 10 },
+ order: [apiPredicate1],
+ filter: [apiPredicate2],
+ requestParams: {
+ 'test_param.1': apiPredicate1,
+ 'test_param.2': apiPredicate2
+ },
+ task: `delete_user __ ${ dateNowISO }`,
+ uiState: {
+ 'test_uiState.1': uiState1,
+ 'test_uiState.2': uiState2
+ },
+ data: {
+ 'test_data.1': data1,
+ 'test_data.2': data2
+ }
+ };
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new ComponentStateImpl({});
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ it('should verify provided value will be correctly assigned', () => {
+ // When
+ const instance = new ComponentStateImpl(partialStateMock);
+
+ // Then
+ expect(instance.id).toEqual(partialStateMock.id);
+ expect(instance.status).toEqual(partialStateMock.status);
+ expect(instance.navigationId).toEqual(partialStateMock.navigationId);
+ expect(instance.error).toEqual(partialStateMock.error);
+ expect(instance.search).toEqual(partialStateMock.search);
+ expect(instance.routePath).toEqual(partialStateMock.routePath);
+ expect(instance.routePathSegments).toBe(partialStateMock.routePathSegments);
+ expect(instance.page).toBe(partialStateMock.page);
+ expect(instance.order).toBe(partialStateMock.order);
+ expect(instance.filter).toBe(partialStateMock.filter);
+ expect(instance.requestParams).toBe(partialStateMock.requestParams);
+ expect(instance.task).toEqual(partialStateMock.task);
+ expect(instance.uiState).toBe(partialStateMock.uiState);
+ expect(instance.data).toBe(partialStateMock.data);
+ });
+
+ it('should verify will correctly assign default values', () => {
+ // When
+ const instance = new ComponentStateImpl({});
+
+ // Then
+ expect(instance).toBeDefined();
+ expect(instance.id).toBeUndefined();
+ expect(instance.status).toEqual(IDLE);
+ expect(instance.navigationId).toEqual(null);
+ expect(instance.routePath).toBeUndefined();
+ expect(instance.routePathSegments).toEqual([]);
+ expect(instance.search).toEqual('');
+ expect(instance.page).toEqual(RequestPageImpl.empty());
+ expect(instance.order).toEqual(RequestOrderImpl.empty());
+ expect(instance.filter).toEqual(RequestFilterImpl.empty());
+ expect(instance.requestParams).toBeInstanceOf(Map);
+ expect(instance.error).toEqual(null);
+ expect(instance.task).toEqual(null);
+ expect(instance.data).toBeInstanceOf(Map);
+ expect(instance.uiState).toBeInstanceOf(Map);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentStateImpl.of(partialStateMock);
+
+ // Then
+ expect(instance.id).toEqual(partialStateMock.id);
+ expect(instance.status).toEqual(partialStateMock.status);
+ expect(instance.navigationId).toEqual(partialStateMock.navigationId);
+ expect(instance.error).toEqual(partialStateMock.error);
+ expect(instance.search).toEqual(partialStateMock.search);
+ expect(instance.routePath).toEqual(partialStateMock.routePath);
+ expect(instance.routePathSegments).toBe(partialStateMock.routePathSegments);
+ expect(instance.page).toBe(partialStateMock.page);
+ expect(instance.order).toBe(partialStateMock.order);
+ expect(instance.filter).toBe(partialStateMock.filter);
+ expect(instance.requestParams).toBe(partialStateMock.requestParams);
+ expect(instance.task).toEqual(partialStateMock.task);
+ expect(instance.uiState).toBe(partialStateMock.uiState);
+ expect(instance.data).toBe(partialStateMock.data);
+ });
+ });
+
+ describe('|fromLiteralComponentState|', () => {
+ it('should verify will create instance of ComponentStateImpl from LiteralComponentState', () => {
+ // When
+ const instance = ComponentStateImpl.fromLiteralComponentState(literalStateMock);
+
+ // Then
+ expect(instance).toEqual(stateMock);
+ expect(instance.order.criteria[0]).toBe(stateMock.order.criteria[0]);
+ expect(instance.filter.criteria[0]).toBe(stateMock.filter.criteria[0]);
+ expect(instance.requestParams.get('test_param.1')).toBe(stateMock.requestParams.get('test_param.1'));
+ expect(instance.requestParams.get('test_param.2')).toBe(stateMock.requestParams.get('test_param.2'));
+ expect(instance.uiState.get('test_uiState.1')).toBe(stateMock.uiState.get('test_uiState.1'));
+ expect(instance.uiState.get('test_uiState.2')).toBe(stateMock.uiState.get('test_uiState.2'));
+ expect(instance.data.get('test_data.1')).toBe(stateMock.data.get('test_data.1'));
+ expect(instance.data.get('test_data.2')).toBe(stateMock.data.get('test_data.2'));
+ });
+ });
+
+ describe('|cloneDeepLiteral|', () => {
+ it('should verify will create deep clone from LiteralComponentState', () => {
+ // When
+ const instance = ComponentStateImpl.cloneDeepLiteral(literalStateMock);
+
+ // Then
+ expect(instance).toEqual(literalStateMock);
+ expect(instance.order[0]).not.toBe(literalStateMock.order[0]);
+ expect(instance.filter[0]).not.toBe(literalStateMock.filter[0]);
+ expect(instance.requestParams['test_param.1']).not.toBe(literalStateMock.requestParams['test_param.1']);
+ expect(instance.requestParams['test_param.2']).not.toBe(literalStateMock.requestParams['test_param.2']);
+ expect(instance.uiState['test_uiState.1']).not.toBe(literalStateMock.uiState['test_uiState.1']);
+ expect(instance.uiState['test_uiState.2']).not.toBe(literalStateMock.uiState['test_uiState.2']);
+ expect(instance.data['test_data.1']).not.toBe(literalStateMock.data['test_data.1']);
+ expect(instance.data['test_data.2']).not.toBe(literalStateMock.data['test_data.2']);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|toLiteral|', () => {
+ it('should verify will create LiteralComponentState', () => {
+ // When
+ const instance = stateMock.toLiteral();
+
+ // Then
+ expect(instance).toEqual(literalStateMock);
+ expect(instance.order[0]).toBe(literalStateMock.order[0]);
+ expect(instance.filter[0]).toBe(literalStateMock.filter[0]);
+ expect(instance.requestParams['test_param.1']).toBe(literalStateMock.requestParams['test_param.1']);
+ expect(instance.requestParams['test_param.2']).toBe(literalStateMock.requestParams['test_param.2']);
+ expect(instance.uiState['test_uiState.1']).toBe(literalStateMock.uiState['test_uiState.1']);
+ expect(instance.uiState['test_uiState.2']).toBe(literalStateMock.uiState['test_uiState.2']);
+ expect(instance.data['test_data.1']).toBe(literalStateMock.data['test_data.1']);
+ expect(instance.data['test_data.2']).toBe(literalStateMock.data['test_data.2']);
+ });
+ });
+
+ describe('|toLiteralDeepClone|', () => {
+ it('should verify will create LiteralComponentState deep cloned', () => {
+ // When
+ const instance = stateMock.toLiteralDeepClone();
+
+ // Then
+ expect(instance).toEqual(literalStateMock);
+ expect(instance.order[0]).not.toBe(literalStateMock.order[0]);
+ expect(instance.filter[0]).not.toBe(literalStateMock.filter[0]);
+ expect(instance.requestParams['test_param.1']).not.toBe(literalStateMock.requestParams['test_param.1']);
+ expect(instance.requestParams['test_param.2']).not.toBe(literalStateMock.requestParams['test_param.2']);
+ expect(instance.uiState['test_uiState.1']).not.toBe(literalStateMock.uiState['test_uiState.1']);
+ expect(instance.uiState['test_uiState.2']).not.toBe(literalStateMock.uiState['test_uiState.2']);
+ expect(instance.data['test_data.1']).not.toBe(literalStateMock.data['test_data.1']);
+ expect(instance.data['test_data.2']).not.toBe(literalStateMock.data['test_data.2']);
+ });
+ });
+
+ describe('|copy|', () => {
+ it('should verify will create copy from ComponentStateImpl', () => {
+ // When
+ const instance = stateMock.copy();
+
+ // Then
+ expect(instance).not.toBe(stateMock);
+ expect(instance).toEqual(stateMock);
+ });
+
+ it('should verify will merge provided State on top of original and return instance', () => {
+ // Given
+ const partialState: Partial = {
+ id: 'newId',
+ status: LOADED,
+ search: 'randomSearch',
+ routePath: 'domain/context/entity/20',
+ routePathSegments: ['domain/context', 'entity/20'],
+ data: new Map(),
+ uiState: new Map()
+ };
+
+ // When
+ const instance = stateMock.copy(partialState);
+
+ // Then
+ expect(instance).not.toBe(stateMock);
+ expect(instance).not.toEqual(stateMock);
+ expect(instance.id).toEqual(partialState.id);
+ expect(instance.status).toEqual(partialState.status);
+ expect(instance.search).toEqual(partialState.search);
+ expect(instance.routePath).toEqual(partialState.routePath);
+ expect(instance.routePathSegments).toBe(partialState.routePathSegments);
+ expect(instance.data).toBe(partialState.data);
+ expect(instance.uiState).toBe(partialState.uiState);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.ts
new file mode 100644
index 0000000000..44c9652104
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-state.model.ts
@@ -0,0 +1,438 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-explicit-any */
+
+import { CollectionsUtil } from '../../../../utils';
+
+import {
+ Copy,
+ Literal,
+ LiteralApiPredicates,
+ LiteralRequestPage,
+ RequestFilter,
+ RequestFilterImpl,
+ RequestOrder,
+ RequestOrderImpl,
+ RequestPage,
+ RequestPageImpl
+} from '../../../../common';
+
+import { IDLE, StatusType } from './component-status.model';
+
+/**
+ * ** Literal Component State in purest format ready for Store persisting.
+ *
+ *
+ */
+export interface LiteralComponentState {
+ /**
+ * ** Identifier for Component State.
+ */
+ readonly id: string;
+
+ /**
+ * ** Status for Component State.
+ */
+ readonly status: StatusType;
+
+ /**
+ * ** Component State Data.
+ *
+ * - Free format Literal Object.
+ *
+ */
+ readonly data?: { [key: string]: any };
+
+ /**
+ * ** Route path for current State.
+ */
+ readonly routePath?: string;
+
+ /**
+ * ** Route path segments for current State.
+ */
+ readonly routePathSegments?: string[];
+
+ /**
+ * ** Search query for Http requests.
+ */
+ readonly search?: string;
+
+ /**
+ * ** Page for Http requests.
+ */
+ readonly page?: LiteralRequestPage;
+
+ /**
+ * ** Order for Http requests.
+ */
+ readonly order?: LiteralApiPredicates;
+
+ /**
+ * ** Filter for Http requests.
+ */
+ readonly filter?: LiteralApiPredicates;
+
+ /**
+ * ** Order for Http requests.
+ */
+ readonly requestParams?: { [key: string]: any };
+
+ /**
+ * ** Task is property that give bi-directional refinement context.
+ *
+ * - Gives context to Effect through Action.
+ * - Gives context to Component through ComponentState (ComponentModel).
+ */
+ readonly task?: string;
+
+ /**
+ * ** Router NavigationId bound to this Component State.
+ */
+ readonly navigationId?: number;
+
+ /**
+ * ** Error that could happen in Stream manipulation down to the Components.
+ *
+ * - Ideal for storing Http errors, so Component could easily leverage that knowledge and show info for User.
+ *
+ */
+ readonly error?: Error;
+
+ /**
+ * ** Component State UiState, that holds all information for UiElements.
+ *
+ * - Free format Literal Object where key identifier could be (Component/Html Element) name/id/class etc...
+ *
+ */
+ readonly uiState?: { [key: string]: any };
+}
+
+export interface ComponentState extends Literal, Copy {
+ /**
+ * ** Identifier for Component State.
+ */
+ readonly id: string;
+
+ /**
+ * ** Status for Component State.
+ */
+ readonly status: StatusType;
+
+ /**
+ * ** Component State Data.
+ *
+ * - Free format Map.
+ *
+ */
+ readonly data?: Map;
+
+ /**
+ * ** Route path for current State.
+ */
+ readonly routePath?: string;
+
+ /**
+ * ** Route Path Segments for current State.
+ */
+ readonly routePathSegments?: string[];
+
+ /**
+ * ** Search query for Http requests.
+ */
+ readonly search?: string;
+
+ /**
+ * ** Page for Http requests.
+ */
+ readonly page?: RequestPage;
+
+ /**
+ * ** Order for Http requests.
+ */
+ readonly order?: RequestOrder;
+
+ /**
+ * ** Filter for Http requests.
+ */
+ readonly filter?: RequestFilter;
+
+ /**
+ * ** Map with different parameters for Http requests.
+ */
+ readonly requestParams?: Map;
+
+ /**
+ * ** Task is property that give bi-directional refinement context.
+ *
+ * - Gives context to Effect through Action.
+ * - Gives context to Component through ComponentState (ComponentModel).
+ */
+ readonly task?: string;
+
+ /**
+ * ** Router NavigationId bound to this Component State.
+ */
+ readonly navigationId?: number;
+
+ /**
+ * ** Error that could happen in Stream manipulation down to the Components.
+ *
+ * - Ideal for storing Http errors, so Component could easily leverage that knowledge and show info for User.
+ *
+ */
+ readonly error?: Error;
+
+ /**
+ * ** Component State UiState, that holds all information for UiElements.
+ *
+ * - Free format Map where key identifier could be (Component/Html Element) name/id/class etc...
+ *
+ */
+ readonly uiState?: Map;
+
+ /**
+ * @inheritDoc
+ */
+ toLiteral(): LiteralComponentState;
+
+ /**
+ * @inheritDoc
+ */
+ toLiteralDeepClone(): LiteralComponentState;
+
+ /**
+ * @inheritDoc
+ */
+ copy(state?: Partial): ComponentState;
+}
+
+/**
+ * ** ComponentState implementation will all methods and other utilities.
+ */
+export class ComponentStateImpl implements ComponentState {
+ /**
+ * @inheritDoc
+ */
+ readonly id: string;
+
+ /**
+ * @inheritDoc
+ */
+ readonly status: StatusType;
+
+ /**
+ * @inheritDoc
+ */
+ readonly data: Map;
+
+ /**
+ * @inheritDoc
+ */
+ readonly routePath: string;
+
+ /**
+ * @inheritDoc
+ */
+ readonly routePathSegments: string[];
+
+ /**
+ * @inheritDoc
+ */
+ readonly search: string;
+
+ /**
+ * @inheritDoc
+ */
+ readonly page: RequestPageImpl;
+
+ /**
+ * @inheritDoc
+ */
+ readonly order: RequestOrderImpl;
+
+ /**
+ * @inheritDoc
+ */
+ readonly filter: RequestFilterImpl;
+
+ /**
+ * @inheritDoc
+ */
+ readonly requestParams: Map;
+
+ /**
+ * @inheritDoc
+ */
+ readonly task: string;
+
+ /**
+ * @inheritDoc
+ */
+ readonly navigationId: number;
+
+ /**
+ * @inheritDoc
+ */
+ readonly error: Error;
+
+ /**
+ * @inheritDoc
+ */
+ readonly uiState: Map;
+
+ /**
+ * ** Constructor.
+ *
+ *
+ * Important:
+ *
+ *
+ * If you add new Property in {@link LiteralComponentState}/{@link ComponentState}
+ *
+ *
+ * Implement field in {@link ComponentStateImpl} and handle null/undefined, assign defaults (required for Collections).
+ *
+ *
+ * Copy/Clone process have to be handled manually (for performance gain) in methods:
+ *
+ * {@link ComponentStateImpl.fromLiteralComponentState}
+ * {@link ComponentStateImpl.cloneDeepLiteral}
+ * {@link ComponentStateImpl.toLiteral}
+ *
+ *
+ *
+ */
+ constructor(stateModelProp: Partial) {
+ const stateModel: Partial = CollectionsUtil.isDefined(stateModelProp)
+ ? stateModelProp
+ : {};
+
+ this.id = stateModel.id;
+ this.status = stateModel.status ?? IDLE;
+ this.navigationId = stateModel.navigationId ?? null;
+ this.routePath = stateModel.routePath;
+ this.routePathSegments = stateModel.routePathSegments ?? [];
+ this.search = stateModel.search ?? '';
+ this.page = stateModel.page ?? RequestPageImpl.empty();
+ this.order = stateModel.order ?? RequestOrderImpl.empty();
+ this.filter = stateModel.filter ?? RequestFilterImpl.empty();
+ this.requestParams = stateModel.requestParams ?? new Map();
+ this.task = stateModel.task ?? null;
+ this.error = stateModel.error ?? null;
+ this.data = stateModel.data ?? new Map();
+ this.uiState = stateModel.uiState ?? new Map();
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(stateModel: Partial): ComponentStateImpl {
+ return new ComponentStateImpl(stateModel);
+ }
+
+ /**
+ * ** Convert provided {@link LiteralComponentState} into instance of {@link ComponentStateImpl}.
+ *
+ * Every literals could be transformed to their original Collection format.
+ *
+ *
+ * Object literals could be transformed to Map/WeakMap/Set depends of the needs.
+ *
+ *
+ * Array is keep as it is.
+ *
+ *
+ *
+ *
+ * @see CollectionsUtil.transformObjectToMap
+ * @see CollectionsUtil.transformMapToObject
+ */
+ static fromLiteralComponentState(literalStateModel: LiteralComponentState): ComponentStateImpl {
+ return ComponentStateImpl.of({
+ ...literalStateModel,
+ page: RequestPageImpl.fromLiteral(literalStateModel.page),
+ order: RequestOrderImpl.fromLiteral(literalStateModel.order),
+ filter: RequestFilterImpl.fromLiteral(literalStateModel.filter),
+ requestParams: CollectionsUtil.transformObjectToMap(literalStateModel.requestParams),
+ data: CollectionsUtil.transformObjectToMap(literalStateModel.data),
+ uiState: CollectionsUtil.transformObjectToMap(literalStateModel.uiState)
+ });
+ }
+
+ /**
+ * ** Make deep clone from Literal Component State.
+ */
+ static cloneDeepLiteral(literalStateModel: LiteralComponentState): LiteralComponentState {
+ return {
+ id: literalStateModel.id,
+ status: literalStateModel.status,
+ data: CollectionsUtil.cloneDeep(literalStateModel.data),
+ routePath: literalStateModel.routePath,
+ routePathSegments: [...literalStateModel.routePathSegments],
+ search: literalStateModel.search,
+ page: CollectionsUtil.cloneDeep(literalStateModel.page),
+ order: CollectionsUtil.cloneDeep(literalStateModel.order),
+ filter: CollectionsUtil.cloneDeep(literalStateModel.filter),
+ requestParams: CollectionsUtil.cloneDeep(literalStateModel.requestParams),
+ task: literalStateModel.task,
+ navigationId: literalStateModel.navigationId,
+ error: literalStateModel.error,
+ uiState: CollectionsUtil.cloneDeep(literalStateModel.uiState)
+ };
+ }
+
+ /**
+ *
+ * Every Collection should be transformed to format of JSON supported literals, ready for LocalStorage/SessionStorage persist.
+ *
+ *
+ * Map/WeakMap/Set have to be transform to Object literal.
+ *
+ *
+ * Array is keep as it is.
+ *
+ *
+ *
+ *
+ * @see CollectionsUtil.transformObjectToMap
+ * @see CollectionsUtil.transformMapToObject
+ *
+ * @inheritDoc
+ */
+ toLiteral(): LiteralComponentState {
+ return {
+ ...this,
+ page: this.page.toLiteral(),
+ order: this.order.toLiteral(),
+ filter: this.filter.toLiteral(),
+ requestParams: CollectionsUtil.transformMapToObject(this.requestParams),
+ data: CollectionsUtil.transformMapToObject(this.data),
+ uiState: CollectionsUtil.transformMapToObject(this.uiState)
+ };
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toLiteralDeepClone(): LiteralComponentState {
+ return ComponentStateImpl.cloneDeepLiteral(
+ this.toLiteral()
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ copy(state: Partial = {}): ComponentStateImpl {
+ return ComponentStateImpl.of({
+ ...this,
+ ...state
+ });
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-status.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-status.model.ts
new file mode 100644
index 0000000000..ccd24bb50b
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/component-status.model.ts
@@ -0,0 +1,38 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/no-inferrable-types */
+
+/**
+ * ** Status constant for Initialized.
+ */
+export const INITIALIZED = 'Initialized';
+
+/**
+ * ** Status constant for Idle.
+ */
+export const IDLE = 'Idle';
+
+/**
+ * ** Status constant for Loading.
+ */
+export const LOADING = 'Loading';
+
+/**
+ * ** Status constant for Loaded.
+ */
+export const LOADED = 'Loaded';
+
+/**
+ * ** Status constant for Failed.
+ */
+export const FAILED = 'Failed';
+
+/**
+ * ** Status types.
+ */
+export type StatusType = typeof INITIALIZED | typeof IDLE | typeof LOADING | typeof LOADED | typeof FAILED;
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.spec.ts
new file mode 100644
index 0000000000..226e6822c2
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.spec.ts
@@ -0,0 +1,311 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation,arrow-body-style,prefer-arrow/prefer-arrow-functions */
+
+import { CollectionsUtil } from '../../../../utils';
+
+import { FAILED, IDLE, LOADED, LOADING } from './component-status.model';
+
+import { ComponentState, ComponentStateImpl, LiteralComponentState } from './component-state.model';
+
+import { ComponentsStateHelper, LiteralComponentsState } from './components-state.model';
+
+describe('ComponentsStateHelper', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new ComponentsStateHelper();
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Methods::', () => {
+ let literalComponentsState: LiteralComponentsState;
+ let helper: ComponentsStateHelper;
+ let componentState1: ComponentState;
+ let componentState2: ComponentState;
+ let componentState3: ComponentState;
+ let componentState4: ComponentState;
+
+ beforeEach(() => {
+ componentState1 = ComponentStateImpl.of({ id: 'component1', status: LOADED });
+ componentState2 = ComponentStateImpl.of({ id: 'component2', status: IDLE });
+ componentState3 = ComponentStateImpl.of({ id: 'component3', status: FAILED });
+ componentState4 = ComponentStateImpl.of({ id: 'component4', status: IDLE });
+
+ literalComponentsState = createComponentsLiteralState(
+ componentState1,
+ componentState2,
+ componentState3,
+ componentState4
+ );
+
+ helper = new ComponentsStateHelper();
+ helper.setState(literalComponentsState);
+ });
+
+ describe('|getState|', () => {
+ it('should verify will return literalComponentState', () => {
+ // When
+ const state = helper.getState();
+
+ // Then
+ expect(state).toBeDefined();
+ expect(state).toEqual(literalComponentsState);
+ expect(state.components).not.toBe(literalComponentsState.components);
+ expect(state.routePathSegments).not.toBe(literalComponentsState.routePathSegments);
+ });
+ });
+
+ describe('|getLiteralComponentState|', () => {
+ it('should verify will return LiteralComponentState', () => {
+ // When
+ const state = helper.getLiteralComponentState('component3', ['test_domain/context', 'test_entity/10']);
+
+ // Then
+ expect(state).toBeDefined();
+ expect(state).toEqual(literalComponentsState
+ .routePathSegments['test_domain/context']
+ .routePathSegments['test_entity/10']
+ .components['component3']);
+ });
+
+ it('should verify will return null if there is no such state', () => {
+ // When
+ const state = helper.getLiteralComponentState('component10', ['test_domain/context', 'test_entity/10']);
+
+ // Then
+ expect(state).toEqual(null);
+ });
+
+ it('should verify will return null if there is no such routePathSegments', () => {
+ // When
+ const state = helper.getLiteralComponentState('component3', ['test_domain/context', 'entity/15']);
+
+ // Then
+ expect(state).toEqual(null);
+ });
+ });
+
+ describe('|getComponentState|', () => {
+ it('should verify will return ComponentState', () => {
+ // When
+ const state = helper.getComponentState('component2', ['test_domain/context', 'test_entity/18']);
+
+ // Then
+ expect(state).toBeDefined();
+ expect(state).toEqual(componentState2.copy({
+ routePath: 'domain/context/entity/18',
+ routePathSegments: ['test_domain/context', 'test_entity/18']
+ }));
+ expect(state.routePathSegments).not.toBe(componentState2.routePathSegments);
+ expect(state.data).not.toBe(componentState2.data);
+ expect(state.uiState).not.toBe(componentState2.uiState);
+ });
+
+ it('should verify will return null if there is no such state', () => {
+ // When
+ const state = helper.getComponentState('component4', ['test_domain/context']);
+
+ // Then
+ expect(state).toEqual(null);
+ });
+
+ it('should verify will return null if there is no such routePathSegments', () => {
+ // When
+ const state = helper.getLiteralComponentState('component2', ['domain/explore', 'test_entity/10']);
+
+ // Then
+ expect(state).toEqual(null);
+ });
+ });
+
+ describe('|getAllComponentState|', () => {
+ it('should verify will return Array of all ComponentState in given routePathSegments', () => {
+ // When
+ const states = helper.getAllComponentState(['test_domain/context', 'test_entity/10']);
+
+ // Then
+ expect(states.length).toEqual(8);
+ expect(states).toEqual([
+ componentState1,
+ componentState3,
+ componentState2.copy({
+ routePath: 'test_domain/context',
+ routePathSegments: ['test_domain/context']
+ }),
+ componentState3.copy({
+ routePath: 'test_domain/context',
+ routePathSegments: ['test_domain/context']
+ }),
+ componentState2.copy({
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ }),
+ componentState4.copy({
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ }),
+ componentState1.copy({
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ }),
+ componentState3.copy({
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ })
+ ]);
+ });
+ });
+
+ describe('|updateLiteralComponentState|', () => {
+ it('should verify will update state in right routePathSegments', () => {
+ // Given
+ const literalState: LiteralComponentState = {
+ ...componentState1.toLiteral(),
+ status: LOADING,
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10'],
+ data: { payload: { content: { data: [1, 2, 3] } } },
+ uiState: { element1: { click: true } }
+ };
+
+ // Then 1
+ expect(helper
+ .getState()
+ .routePathSegments['test_domain/context']
+ .routePathSegments['test_entity/10']
+ .components['component1']).not.toEqual(literalState);
+
+ // When
+ helper.updateLiteralComponentState(literalState);
+
+ // Then 2
+ expect(helper
+ .getState()
+ .routePathSegments['test_domain/context']
+ .routePathSegments['test_entity/10']
+ .components['component1']).toEqual(literalState);
+ });
+ });
+
+ describe('|resetComponentStates|', () => {
+ it('should verify will reset all ComponentStata status in given routePathSegments', () => {
+ // When
+ helper.resetComponentStates(['test_domain/context', 'test_entity/10']);
+ const state = helper.getState();
+
+ // Then
+ CollectionsUtil.iterateObject(state.components, (value) => {
+ expect(value.status).toEqual(IDLE);
+ });
+
+ CollectionsUtil.iterateObject(state.routePathSegments['test_domain/context'].components, (value) => {
+ expect(value.status).toEqual(IDLE);
+ });
+
+ CollectionsUtil.iterateObject(
+ state.routePathSegments['test_domain/context'].routePathSegments['test_entity/10'].components,
+ (value) => {
+ expect(value.status).toEqual(IDLE);
+ }
+ );
+ });
+ });
+
+ describe('|deleteRoutePathSegments|', () => {
+ it('should verify will delete routePathSegment', () => {
+ // Then 1
+ expect(helper.getState().routePathSegments['test_domain/context'].routePathSegments['test_entity/18']).toBeDefined();
+
+ // When
+ helper.deleteRoutePathSegments(['test_domain/context', 'test_entity/18']);
+
+ // Then 2
+ expect(helper.getState().routePathSegments['test_domain/context'].routePathSegments['test_entity/18']).toBeUndefined();
+ });
+ });
+ });
+});
+
+const createComponentsLiteralState = (c1: ComponentState, c2: ComponentState, c3: ComponentState, c4: ComponentState) => {
+ return {
+ components: {
+ component1: { ...c1.toLiteral() },
+ component3: { ...c3.toLiteral() }
+ },
+ routePathSegments: {
+ 'test_domain/context': {
+ components: {
+ component2: {
+ ...c2.toLiteral(),
+ routePath: 'test_domain/context',
+ routePathSegments: ['test_domain/context']
+ },
+ component3: {
+ ...c3.toLiteral(),
+ routePath: 'test_domain/context',
+ routePathSegments: ['test_domain/context']
+ }
+ },
+ routePathSegments: {
+ 'test_entity/10': {
+ components: {
+ component2: {
+ ...c2.toLiteral(),
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ },
+ component4: {
+ ...c4.toLiteral(),
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ },
+ component1: {
+ ...c1.toLiteral(),
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ },
+ component3: {
+ ...c3.toLiteral(),
+ routePath: 'domain/context/entity/10',
+ routePathSegments: ['test_domain/context', 'test_entity/10']
+ }
+ },
+ routePathSegments: {}
+ },
+ 'test_entity/18': {
+ components: {
+ component3: {
+ ...c3.toLiteral(),
+ routePath: 'domain/context/entity/18',
+ routePathSegments: ['test_domain/context', 'test_entity/18']
+ },
+ component2: {
+ ...c2.toLiteral(),
+ routePath: 'domain/context/entity/18',
+ routePathSegments: ['test_domain/context', 'test_entity/18']
+ },
+ component1: {
+ ...c1.toLiteral(),
+ routePath: 'domain/context/entity/18',
+ routePathSegments: ['test_domain/context', 'test_entity/18']
+ },
+ component4: {
+ ...c4.toLiteral(),
+ routePath: 'domain/context/entity/18',
+ routePathSegments: ['test_domain/context', 'test_entity/18']
+ }
+ },
+ routePathSegments: {}
+ }
+ }
+ }
+ }
+ } as LiteralComponentsState;
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.ts
new file mode 100644
index 0000000000..2620df6f48
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/components-state.model.ts
@@ -0,0 +1,283 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../../../utils';
+
+import { IDLE } from './component-status.model';
+import { ComponentState, ComponentStateImpl, LiteralComponentState } from './component-state.model';
+
+export interface LiteralComponentsState {
+ readonly components: { [name: string]: LiteralComponentState };
+ readonly routePathSegments: { [segmentId: string]: LiteralComponentsState };
+}
+
+/**
+ * ** ComponentsState Helper.
+ *
+ *
+ */
+export class ComponentsStateHelper {
+ private _literalComponentsState: LiteralComponentsState;
+
+ constructor() {
+ this._literalComponentsState = {
+ components: {},
+ routePathSegments: {}
+ };
+ }
+
+ /**
+ * ** Returns LiteralComponentsState from Helper.
+ */
+ getState(): LiteralComponentsState {
+ return {
+ ...this._literalComponentsState
+ };
+ }
+
+ /**
+ * ** Will set state to the local Helper state.
+ */
+ setState(literalComponentsState: LiteralComponentsState) {
+ this._literalComponentsState = this._shallowCloneComponentsState(literalComponentsState);
+
+ return this;
+ }
+
+ /**
+ * ** Will return LiteralComponentState for given id and routePathSegments.
+ */
+ getLiteralComponentState(id: string, routePathSegments?: string[]): LiteralComponentState {
+ return this._getLiteralComponentState(
+ id,
+ CollectionsUtil.isArray(routePathSegments)
+ ? [...routePathSegments]
+ : [],
+ this._literalComponentsState
+ );
+ }
+
+ /**
+ * ** Get ComponentState for given id and routePathSegments.
+ */
+ getComponentState(id: string, routePathSegments?: string[]): ComponentState {
+ const literalComponentState = this._getLiteralComponentState(
+ id,
+ CollectionsUtil.isArray(routePathSegments)
+ ? [...routePathSegments]
+ : [],
+ this._literalComponentsState
+ );
+
+ return CollectionsUtil.isDefined(literalComponentState)
+ ? ComponentStateImpl.fromLiteralComponentState(literalComponentState)
+ : null;
+ }
+
+ /**
+ * ** Get all ComponentState for given routePathSegments.
+ */
+ getAllComponentState(routePathSegments: string[]): ComponentState[] {
+ return this._getAllComponentState(
+ CollectionsUtil.isArray(routePathSegments)
+ ? [...routePathSegments]
+ : [],
+ this._literalComponentsState
+ );
+ }
+
+ /**
+ * ** Update LiteralComponentState.
+ */
+ updateLiteralComponentState(literalComponentState: LiteralComponentState): void {
+ return this._updateLiteralComponentState(
+ literalComponentState,
+ [...literalComponentState.routePathSegments],
+ this._literalComponentsState
+ );
+ }
+
+ /**
+ * ** Reset component status to NOT_LOADED for all ComponentState in a given routePathSegment.
+ */
+ resetComponentStates(routePathSegments: string[]): void {
+ this._resetComponentStates(
+ CollectionsUtil.isArray(routePathSegments)
+ ? [...routePathSegments]
+ : [],
+ this._literalComponentsState
+ );
+ }
+
+ /**
+ * ** Delete all ComponentState for given routePathSegment.
+ */
+ deleteRoutePathSegments(routePathSegments: string[]): void {
+ this._deleteRoutePathSegments(
+ CollectionsUtil.isArray(routePathSegments)
+ ? [...routePathSegments]
+ : [],
+ this._literalComponentsState
+ );
+ }
+
+ /**
+ * ** Update ComponentState.
+ */
+ private _updateLiteralComponentState(
+ literalComponentState: LiteralComponentState,
+ routePathSegments: string[],
+ state: LiteralComponentsState): void {
+
+ if (CollectionsUtil.isArrayEmpty(routePathSegments)) {
+ state.components[literalComponentState.id] = literalComponentState;
+
+ return;
+ }
+
+ const routePathSegment = routePathSegments.shift();
+
+ this._updateLiteralComponentState(
+ literalComponentState,
+ routePathSegments,
+ this._normalizeRoutePathSegments(state.routePathSegments, routePathSegment)
+ );
+ }
+
+ /**
+ * ** Get ComponentState.
+ */
+ private _getLiteralComponentState(
+ id: string,
+ routePathSegments: string[],
+ state: LiteralComponentsState): LiteralComponentState | null {
+
+ if (!state) {
+ return null;
+ }
+
+ if (CollectionsUtil.isArrayEmpty(routePathSegments)) {
+ if (state.components[id]) {
+ return ComponentStateImpl.cloneDeepLiteral(state.components[id]);
+ }
+
+ return null;
+ }
+
+ const routePathSegment = routePathSegments.shift();
+
+ return this._getLiteralComponentState(
+ id,
+ routePathSegments,
+ state.routePathSegments[routePathSegment]
+ );
+ }
+
+ /**
+ * ** Get all components for given routePathSegments.
+ */
+ private _getAllComponentState(routePathSegments: string[], state: LiteralComponentsState): ComponentState[] {
+ if (!state) {
+ return [];
+ }
+
+ const components: ComponentState[] = CollectionsUtil.objectValues(state.components)
+ .map((c) => ComponentStateImpl.fromLiteralComponentState(
+ ComponentStateImpl.cloneDeepLiteral(c)
+ ));
+
+ if (CollectionsUtil.isArrayEmpty(routePathSegments)) {
+ return components;
+ }
+
+ const routePathSegment = routePathSegments.shift();
+
+ return [
+ ...components,
+ ...this._getAllComponentState(routePathSegments, state.routePathSegments[routePathSegment])
+ ];
+ }
+
+ /**
+ * ** Reset component status to NOT_LOADED for all component in a given context.
+ */
+ private _resetComponentStates(routePathSegments: string[], state: LiteralComponentsState): void {
+ CollectionsUtil.iterateObject(state.components, (componentState, id) => {
+ state.components[id] = { ...componentState, status: IDLE };
+ });
+
+ if (CollectionsUtil.isArrayEmpty(routePathSegments)) {
+ return;
+ }
+
+ const routePathSegment = routePathSegments.shift();
+
+ this._resetComponentStates(
+ routePathSegments,
+ this._normalizeRoutePathSegments(state.routePathSegments, routePathSegment)
+ );
+ }
+
+ /**
+ * ** Delete all components state for a given route path segment.
+ */
+ private _deleteRoutePathSegments(routePathSegments: string[], state: LiteralComponentsState): void {
+ const routePathSegment = routePathSegments.shift();
+
+ if (!routePathSegment) {
+ return;
+ }
+
+ if (CollectionsUtil.isArrayEmpty(routePathSegments)) {
+ delete state.routePathSegments[routePathSegment];
+
+ return;
+ }
+
+ this._deleteRoutePathSegments(
+ routePathSegments,
+ this._normalizeRoutePathSegments(state.routePathSegments, routePathSegment)
+ );
+ }
+
+ /**
+ * ** Normalize Route path segments.
+ */
+ private _normalizeRoutePathSegments(
+ urlSegments: { [segmentId: string]: LiteralComponentsState },
+ urlSegmentName: string): LiteralComponentsState {
+
+ if (CollectionsUtil.isNil(urlSegments[urlSegmentName])) {
+ urlSegments[urlSegmentName] = {
+ components: {},
+ routePathSegments: {}
+ };
+ }
+
+ return urlSegments[urlSegmentName];
+ }
+
+ private _shallowCloneComponentsState(source: LiteralComponentsState, target?: LiteralComponentsState): LiteralComponentsState {
+ const _source: LiteralComponentsState = source ?? { components: {}, routePathSegments: {} };
+ const _target: LiteralComponentsState = target ?? { components: {}, routePathSegments: {} };
+
+ CollectionsUtil.iterateObject(_source.components, (value, key) => {
+ _target.components[key] = value;
+ });
+
+ CollectionsUtil.iterateObject(_source.routePathSegments, (value, key) => {
+ _target.routePathSegments[key] = {
+ components: {},
+ routePathSegments: {}
+ };
+
+ this._shallowCloneComponentsState(value, _target.routePathSegments[key]);
+ });
+
+ return _target;
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/index.ts
new file mode 100644
index 0000000000..c6369edf04
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/model/state/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component-status.model';
+export * from './component-state.model';
+export * from './components-state.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.spec.ts
new file mode 100644
index 0000000000..ce79f1e9e3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.spec.ts
@@ -0,0 +1,734 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation,arrow-body-style,prefer-arrow/prefer-arrow-functions */
+
+import { TestBed } from '@angular/core/testing';
+
+import { Store } from '@ngrx/store';
+import { RouterReducerState } from '@ngrx/router-store';
+
+import { of, throwError } from 'rxjs';
+
+import { marbles } from 'rxjs-marbles/jasmine';
+
+import { CollectionsUtil } from '../../../utils';
+
+import { GenericAction, STORE_COMPONENTS, STORE_ROUTER, StoreState } from '../../ngrx';
+import { RouterService, RouterState, RouteSegments, RouteState } from '../../router';
+
+import {
+ ComponentModel,
+ ComponentState,
+ ComponentStateImpl,
+ FAILED,
+ IDLE,
+ INITIALIZED,
+ LiteralComponentsState,
+ LiteralComponentState,
+ LOADED,
+ LOADING,
+ StatusType
+} from '../model';
+import { ComponentIdle, ComponentInit, ComponentLoading, ComponentUpdate } from '../state';
+
+import { ComponentService, ComponentServiceImpl } from './component.service';
+
+describe('ComponentService -> ComponentServiceImpl', () => {
+ let storeStub$: jasmine.SpyObj>;
+ let routerServiceStub: jasmine.SpyObj;
+
+ let service: ComponentService;
+
+ beforeEach(() => {
+ storeStub$ = jasmine.createSpyObj>('store', ['select', 'dispatch']);
+ routerServiceStub = jasmine.createSpyObj('routerService', ['get', 'getState']);
+
+ TestBed.configureTestingModule({
+ providers: [
+ { provide: Store, useValue: storeStub$ },
+ { provide: RouterService, useValue: routerServiceStub },
+ { provide: ComponentService, useClass: ComponentServiceImpl }
+ ]
+ });
+
+ service = TestBed.inject(ComponentService);
+ });
+
+ it('should verify service is created', () => {
+ // Then
+ expect(service).toBeDefined();
+ });
+
+ describe('Methods::', () => {
+ let id: string;
+ let components: LiteralComponentsState;
+ let router: RouterState;
+
+ beforeEach(() => {
+ id = 'testComponent10';
+ router = createStoreState()[STORE_ROUTER];
+
+ routerServiceStub.getState.and.returnValue(of(router.state));
+ routerServiceStub.get.and.returnValue(of(router));
+ });
+
+ describe('|init|', () => {
+ it('should verify will initialize correct component and dispatch ComponentInit', () => {
+ // Given
+ const componentState = createComponentState(router, id, INITIALIZED);
+
+ storeStub$.select.and.callFake((param) => {
+ if (param === STORE_COMPONENTS) {
+ components = createStoreState(componentState.toLiteral())[STORE_COMPONENTS];
+
+ return of(components);
+ }
+
+ return of(createStoreState());
+ });
+
+ // When
+ const response$ = service.init(id, router.state);
+
+ // Then
+ expect(storeStub$.select.calls.argsFor(0)).toEqual([jasmine.any(Function)]);
+ expect(storeStub$.select.calls.argsFor(1)).toEqual([STORE_COMPONENTS]);
+ expect(storeStub$.dispatch).toHaveBeenCalledWith(ComponentInit.of(componentState));
+
+ response$.subscribe((model) => {
+ expect(model['_routerState']).toEqual(router);
+ expect(model['_componentState']).toEqual(getComponentState(components, id));
+ expect(model.status).toEqual(INITIALIZED);
+ });
+
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ expect(storeStub$.select.calls.argsFor(2)).toEqual([STORE_COMPONENTS]);
+ expect(routerServiceStub.get).toHaveBeenCalled();
+ }, 1000);
+
+ it('should verify wont dispatch ComponentInit', () => {
+ // Given
+ const componentState = createComponentState(router, id, LOADED);
+
+ storeStub$.select.and.callFake((param) => {
+ if (param === STORE_COMPONENTS) {
+ components = createStoreState(componentState.toLiteral())[STORE_COMPONENTS];
+
+ return of(components);
+ }
+
+ return of(createStoreState(componentState.toLiteral()));
+ });
+
+ // When
+ const response$ = service.init(id, router.state);
+
+ // Then
+ expect(storeStub$.select.calls.argsFor(0)).toEqual([jasmine.any(Function)]);
+ expect(storeStub$.dispatch).not.toHaveBeenCalled();
+ expect(storeStub$.select.calls.argsFor(1)).toEqual([STORE_COMPONENTS]);
+
+ response$.subscribe((model) => {
+ expect(model['_routerState']).toEqual(router);
+ expect(model['_componentState']).toEqual(getComponentState(components, id));
+ expect(model.status).toEqual(LOADED);
+ });
+
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ expect(storeStub$.select.calls.argsFor(2)).toEqual([STORE_COMPONENTS]);
+ expect(routerServiceStub.get).toHaveBeenCalled();
+ }, 1000);
+ });
+
+ describe('|idle|', () => {
+ it('should verify will dispatch ComponentIdle with provided ComponentState', () => {
+ // Given
+ const componentState = createComponentState(router, id, LOADED);
+
+ // When
+ service.idle(componentState);
+
+ // Then
+ expect(storeStub$.dispatch).toHaveBeenCalledWith(ComponentIdle.of(componentState));
+ });
+ });
+
+ describe('|load|', () => {
+ it('should verify will dispatch ComponentLoading with provided ComponentState', () => {
+ // Given
+ const componentState = createComponentState(router, id, FAILED);
+ routerServiceStub.get.and.returnValue(of(router));
+ storeStub$.select
+ .and
+ .returnValue(
+ of(createStoreState(createComponentState(router, id, LOADED).toLiteral())[STORE_COMPONENTS])
+ );
+
+ // When
+ const response$ = service.load(componentState);
+
+ // Then
+ expect(routerServiceStub.get).toHaveBeenCalledTimes(1);
+ // @ts-ignore
+ expect(storeStub$.dispatch.calls.argsFor(0)).toEqual([
+ ComponentLoading.of(
+ componentState.copy({
+ navigationId: router.navigationId,
+ status: LOADING
+ })
+ )
+ ]);
+
+ const assertionComponentState = createComponentState(router, id, LOADED);
+ response$.subscribe((model) => {
+ expect(model['_routerState']).toEqual(router);
+ expect(model['_componentState']).toEqual(assertionComponentState);
+ expect(model.status).toEqual(LOADED);
+ });
+
+ // @ts-ignore
+ expect(storeStub$.select).toHaveBeenCalledWith(STORE_COMPONENTS);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ expect(routerServiceStub.get).toHaveBeenCalledTimes(2);
+ }, 1000);
+
+ it('should verify will dispatch ComponentIdle and ComponentLoading with provided component id', () => {
+ // Given
+ const componentState = createComponentState(router, id, INITIALIZED);
+ routerServiceStub.get.and.returnValue(of(router));
+ storeStub$.select
+ .and
+ .returnValue(
+ of(createStoreState(createComponentState(router, id, LOADED).toLiteral())[STORE_COMPONENTS])
+ );
+
+ // When
+ const response$ = service.load(componentState);
+
+ // Then
+ expect(routerServiceStub.get).toHaveBeenCalledTimes(1);
+ // @ts-ignore
+ expect(storeStub$.dispatch.calls.argsFor(0)).toEqual([
+ ComponentIdle.of(
+ createComponentState(router, id, IDLE).copy({
+ navigationId: router.navigationId
+ })
+ )
+ ]);
+ expect(storeStub$.dispatch.calls.argsFor(1)).toEqual([
+ ComponentLoading.of(
+ createComponentState(router, id, LOADING).copy({
+ navigationId: router.navigationId
+ })
+ )
+ ]);
+
+ const assertionComponentState = createComponentState(router, id, LOADED);
+ response$.subscribe((model) => {
+ expect(model['_routerState']).toEqual(router);
+ expect(model['_componentState']).toEqual(assertionComponentState);
+ expect(model.status).toEqual(LOADED);
+ });
+
+ // @ts-ignore
+ expect(storeStub$.select).toHaveBeenCalledWith(STORE_COMPONENTS);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ expect(routerServiceStub.get).toHaveBeenCalledTimes(2);
+ }, 1000);
+ });
+
+ describe('|update|', () => {
+ it('should verify will dispatch ComponentUpdate with provided ComponentState', () => {
+ // Given
+ const componentState = createComponentState(router, id, LOADED);
+
+ // When
+ service.update(componentState);
+
+ // Then
+ expect(routerServiceStub.get).toHaveBeenCalled();
+ expect(storeStub$.dispatch).toHaveBeenCalledWith(
+ ComponentUpdate.of(
+ componentState.copy({ navigationId: router.navigationId })
+ )
+ );
+ });
+ });
+
+ describe('|hasInSegment|', () => {
+ it('should verify will return true when ComponentState exist', marbles((m) => {
+ // Given
+ const componentsStream$ = m.cold('---a', {
+ a: createStoreState(
+ createComponentState(router, 'someComponentId', LOADED).toLiteral())[STORE_COMPONENTS]
+ });
+ const routeStateStream$ = m.cold('--a', {
+ a: router.state
+ });
+ storeStub$.select.and.returnValue(componentsStream$);
+ routerServiceStub.getState.and.returnValue(routeStateStream$);
+ const expected$ = m.cold('---(a|)', { a: true });
+
+ // When
+ const response$ = service.hasInSegment('someComponentId', ['test_domain/context', 'test_entity/10']);
+
+ // Then
+ m.expect(response$).toBeObservable(expected$);
+ }));
+
+ it('should verify will return false when ComponentState not exist', marbles((m) => {
+ // Given
+ const componentsStream$ = m.cold('---a', {
+ a: createStoreState(
+ createComponentState(router, 'someComponentId', LOADED).toLiteral())[STORE_COMPONENTS]
+ });
+ const routeStateStream$ = m.cold('---a', {
+ a: router.state
+ });
+ storeStub$.select.and.returnValue(componentsStream$);
+ routerServiceStub.getState.and.returnValue(routeStateStream$);
+ const expected$ = m.cold('---(a|)', { a: false });
+
+ // When
+ const response$ = service.hasInSegment('newComponentId', ['test_domain/context', 'test_entity/10']);
+
+ // Then
+ m.expect(response$).toBeObservable(expected$);
+ }));
+ });
+
+ describe('|onInit|', () => {
+ it(`should verify wont emit when state doesn't exist`, marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, 'testId10', INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, 'testId10', IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, 'testId10', LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, 'testId10', LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, 'testId22', LOADED).toLiteral());
+ const cF = createStoreState(createComponentState(router, 'testId33', LOADED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc-d-e--f-|', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS],
+ f: cF[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('--------------|');
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+
+ // When
+ const response$ = service.onInit(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit when state exist', () => {
+ // Given
+ const componentState = createComponentState(router, id, INITIALIZED);
+ const storeState = createStoreState(componentState.toLiteral());
+
+ storeStub$.select.and.returnValue(of(storeState[STORE_COMPONENTS]));
+
+ // When
+ const response$ = service.onInit(id, router.state.routePathSegments);
+
+ // Then
+ // @ts-ignore
+ expect(storeStub$.select.calls.argsFor(0)).toEqual([STORE_COMPONENTS]);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+
+ response$.subscribe((model) => {
+ expect(model).toBeDefined();
+ expect(model.status).toEqual(INITIALIZED);
+ });
+
+ expect(storeStub$.select.calls.argsFor(1)).toEqual([STORE_COMPONENTS]);
+ expect(routerServiceStub.get).toHaveBeenCalled();
+ }, 1000);
+ });
+
+ describe('|onLoaded|', () => {
+ it(`should verify wont emit when state doesn't exist`, marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, 'testId1', INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, 'testId1', IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, 'testId1', LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, 'testId1', LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, 'testId2', LOADED).toLiteral());
+ const cF = createStoreState(createComponentState(router, 'testId3', LOADED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d--e----f-|', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS],
+ f: cF[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('-------------------|');
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+
+ // When
+ const response$ = service.onLoaded(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify wont emit when state status is not LOADED or FAILED', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---|', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('---------|');
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+
+ // When
+ const response$ = service.onLoaded(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit when state exist and its status is LOADED', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, id, LOADED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d---', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS]
+ });
+ const componentsMarbleStream1$ = m.cold('d----', {
+ d: cD[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('---------(a|)', {
+ a: ComponentModel.of(getComponentState(cD, id), cD[STORE_ROUTER])
+ });
+ let cnt = 0;
+ storeStub$.select.and.callFake(() => {
+ cnt++;
+ if (cnt === 1) {
+ return componentsMarbleStream$;
+ }
+
+ if (cnt === 2) {
+ return componentsMarbleStream1$;
+ }
+
+ return throwError(() => new Error(`This shouldn't be reachable`));
+ });
+ routerServiceStub.getState.and.returnValue(m.cold('a----', { a: router.state }));
+ routerServiceStub.get.and.returnValue(m.cold('a----', { a: router }));
+
+ // When
+ const response$ = service.onLoaded(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit when state exist and its status is FAILED', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, id, FAILED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d---', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS]
+ });
+ const componentsMarbleStream1$ = m.cold('d----', {
+ d: cD[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('---------(a|)', {
+ a: ComponentModel.of(getComponentState(cD, id), cD[STORE_ROUTER])
+ });
+ let cnt = 0;
+ storeStub$.select.and.callFake(() => {
+ cnt++;
+ if (cnt === 1) {
+ return componentsMarbleStream$;
+ }
+
+ if (cnt === 2) {
+ return componentsMarbleStream1$;
+ }
+
+ return throwError(() => new Error(`This shouldn't be reachable`));
+ });
+ routerServiceStub.getState.and.returnValue(m.cold('a----', { a: router.state }));
+ routerServiceStub.get.and.returnValue(m.cold('a----', { a: router }));
+
+ // When
+ const response$ = service.onLoaded(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+ });
+
+ describe('|getModel|', () => {
+ it(`should verify wont emit when state doesn't exist`, marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, 'testId21', INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, 'testId21', IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, 'testId21', LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, 'testId21', LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, 'testId32', LOADED).toLiteral());
+ const cF = createStoreState(createComponentState(router, 'testId33', LOADED).toLiteral());
+ const componentsMarbleStream$ = m.cold('--a-bc---d----e--f---|', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS],
+ f: cF[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('---------------------|');
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+
+ // When
+ const response$ = service.getModel(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit on every status', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, id, LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, id, FAILED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d---e-', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('-a--bc---d---e-', {
+ a: ComponentModel.of(getComponentState(cA, id), cA[STORE_ROUTER]),
+ b: ComponentModel.of(getComponentState(cB, id), cB[STORE_ROUTER]),
+ c: ComponentModel.of(getComponentState(cC, id), cC[STORE_ROUTER]),
+ d: ComponentModel.of(getComponentState(cD, id), cD[STORE_ROUTER]),
+ e: ComponentModel.of(getComponentState(cE, id), cE[STORE_ROUTER])
+ });
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+ routerServiceStub.get.and.returnValue(m.cold('a', { a: router }));
+
+ // When
+ const response$ = service.getModel(id, router.state.routePathSegments, ['*']);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit on requested statuses', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, id, LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, id, FAILED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d---e-', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('-----c-------e-', {
+ c: ComponentModel.of(getComponentState(cC, id), cC[STORE_ROUTER]),
+ e: ComponentModel.of(getComponentState(cE, id), cE[STORE_ROUTER])
+ });
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+ routerServiceStub.get.and.returnValue(m.cold('a', { a: router }));
+
+ // When
+ const response$ = service.getModel(id, router.state.routePathSegments, [LOADING, FAILED]);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+
+ it('should verify will emit on default statuses', marbles((m) => {
+ // Given
+ const cA = createStoreState(createComponentState(router, id, INITIALIZED).toLiteral());
+ const cB = createStoreState(createComponentState(router, id, IDLE).toLiteral());
+ const cC = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cD = createStoreState(createComponentState(router, id, LOADED).toLiteral());
+ const cE = createStoreState(createComponentState(router, id, LOADING).toLiteral());
+ const cF = createStoreState(createComponentState(router, id, FAILED).toLiteral());
+ const componentsMarbleStream$ = m.cold('-a--bc---d---e---f-', {
+ a: cA[STORE_COMPONENTS],
+ b: cB[STORE_COMPONENTS],
+ c: cC[STORE_COMPONENTS],
+ d: cD[STORE_COMPONENTS],
+ e: cE[STORE_COMPONENTS],
+ f: cF[STORE_COMPONENTS]
+ });
+ const expectedModelMarbleStream$ = m.cold('---------d-------f-', {
+ d: ComponentModel.of(getComponentState(cD, id), cD[STORE_ROUTER]),
+ f: ComponentModel.of(getComponentState(cF, id), cF[STORE_ROUTER])
+ });
+ storeStub$.select.and.returnValue(componentsMarbleStream$);
+ routerServiceStub.get.and.returnValue(m.cold('a', { a: router }));
+
+ // When
+ const response$ = service.getModel(id, router.state.routePathSegments);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedModelMarbleStream$);
+ }));
+ });
+
+ describe('|dispatchAction|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const componentState = createComponentState(router, id, LOADING);
+ const typeStub = '[component] Some Action';
+ const modelStub = {
+ getComponentState: () => componentState
+ } as ComponentModel;
+ const getModelSpy = spyOn(service, 'getModel').and.returnValue(of(modelStub));
+ const actionStub = { type: typeStub, payload: componentState, task: undefined };
+ const genericActionOfSpy = spyOn(GenericAction, 'of').and.returnValue(actionStub);
+
+ // When
+ service.dispatchAction(typeStub, componentState);
+
+ // Then
+ expect(getModelSpy).toHaveBeenCalledWith(componentState.id, componentState.routePathSegments, ['*']);
+ expect(genericActionOfSpy).toHaveBeenCalledWith(typeStub, componentState, undefined);
+ expect(storeStub$.dispatch).toHaveBeenCalledWith(actionStub);
+ });
+
+ it('should verify will invoke correct methods when Task is provided', () => {
+ // Given
+ const taskIdentifier = `search_data __ ${ new Date().toISOString() }`;
+ const componentState = createComponentState(router, id, INITIALIZED);
+ const typeStub = '[component] Load Users';
+ const modelStub = {
+ getComponentState: () => componentState
+ } as ComponentModel;
+ const getModelSpy = spyOn(service, 'getModel').and.returnValue(of(modelStub));
+ const actionStub = {
+ type: typeStub,
+ payload: componentState,
+ task: taskIdentifier
+ };
+ const genericActionOfSpy = spyOn(GenericAction, 'of').and.returnValue(actionStub);
+
+ // When
+ service.dispatchAction(typeStub, componentState, taskIdentifier);
+
+ // Then
+ expect(getModelSpy).toHaveBeenCalledWith(componentState.id, componentState.routePathSegments, ['*']);
+ expect(genericActionOfSpy).toHaveBeenCalledWith(typeStub, componentState, taskIdentifier);
+ expect(storeStub$.dispatch).toHaveBeenCalledWith(actionStub);
+ });
+ });
+ });
+});
+
+const createComponentState = (router: RouterReducerState, id: string, status: StatusType) => {
+ return ComponentStateImpl.of({
+ id,
+ status,
+ routePath: router?.state.routePath ?? null,
+ routePathSegments: router?.state.routePathSegments ?? [],
+ navigationId: router.navigationId
+ });
+};
+
+const getComponentState = (state: StoreState | LiteralComponentsState, id: string): ComponentState => {
+ if (CollectionsUtil.isNil(state)) {
+ return ComponentStateImpl.of({});
+ }
+
+ const componentsState = (
+ CollectionsUtil.isNil((state as LiteralComponentsState).routePathSegments)
+ ? state[STORE_COMPONENTS]
+ : state
+ ) as LiteralComponentsState;
+
+ return ComponentStateImpl.fromLiteralComponentState(
+ componentsState
+ .routePathSegments['test_domain/context']
+ .routePathSegments['test_entity/10']
+ .components[id]
+ );
+};
+
+const createStoreState = (state?: LiteralComponentState): StoreState => {
+ return CollectionsUtil.isNil(state)
+ ? {
+ [STORE_ROUTER]: RouterState.of(
+ RouteState.of(
+ RouteSegments.of(
+ 'test_entity/10',
+ {},
+ { entity: 10 },
+ null,
+ RouteSegments.of('test_domain/context')
+ ),
+ '/domain/context/entity/10'
+ ),
+ 3
+ ),
+ [STORE_COMPONENTS]: {
+ components: {},
+ routePathSegments: {}
+ }
+ }
+ : {
+ [STORE_ROUTER]: RouterState.of(
+ RouteState.of(
+ RouteSegments.of(
+ 'test_entity/10',
+ {},
+ { entity: 10 },
+ null,
+ RouteSegments.of('test_domain/context')
+ ),
+ '/domain/context/entity/10'
+ ),
+ 3
+ ),
+ [STORE_COMPONENTS]: {
+ components: {},
+ routePathSegments: {
+ 'test_domain/context': {
+ components: {},
+ routePathSegments: {
+ 'test_entity/10': {
+ components: {
+ [state.id]: state
+ },
+ routePathSegments: {}
+ }
+ }
+ }
+ }
+ }
+ };
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.ts
new file mode 100644
index 0000000000..454995e8b6
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/component.service.ts
@@ -0,0 +1,396 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/unified-signatures,ngrx/avoid-mapping-selectors */
+
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { filter, map, switchMap, take, withLatestFrom } from 'rxjs/operators';
+
+import { Store } from '@ngrx/store';
+
+import { CollectionsUtil } from '../../../utils';
+
+import { RouterService, RouterState, RouteState } from '../../router';
+import { GenericAction, STORE_COMPONENTS, StoreState } from '../../ngrx';
+
+import { ComponentIdle, ComponentInit, ComponentLoading, ComponentUpdate } from '../state';
+import {
+ ComponentsStateHelper,
+ ComponentState,
+ ComponentStateImpl,
+ FAILED,
+ IDLE,
+ INITIALIZED,
+ LiteralComponentsState,
+ LOADED,
+ LOADING,
+ StatusType
+} from '../model/state';
+import { ComponentModel } from '../model';
+
+/**
+ * ** Service that manage Components State.
+ *
+ *
+ */
+export abstract class ComponentService {
+ /**
+ * ** Initialize Component State and return Model.
+ */
+ abstract init(id: string, routeState: RouteState): Observable;
+
+ /**
+ * ** Set Component status to IDLE.
+ */
+ abstract idle(componentState: ComponentState): void;
+
+ /**
+ * ** Load Component State and return Model.
+ */
+ abstract load(componentState: ComponentState): Observable;
+
+ /**
+ * ** Update Component State.
+ */
+ abstract update(componentState: ComponentState): void;
+
+ /**
+ * ** Acknowledge if has ComponentState in segment.
+ *
+ * - true - has ComponentState.
+ * - false - doesn't have ComponentState.
+ */
+ abstract hasInSegment(id: string, routePathSegments: string[]): Observable;
+
+ /**
+ * ** Listener that fires once after successful Component State initialization and returns Model.
+ */
+ abstract onInit(id: string, routePathSegments: string[]): Observable;
+
+ /**
+ * ** Listener that fires once after successful Component State load and returns Model.
+ */
+ abstract onLoaded(id: string, routePathSegments: string[]): Observable;
+
+ /**
+ * ** Returns stream with value Component Model and fires whenever Component State changes in Store.
+ *
+ * - If no statusWatch provided by default will listen for statuses {@link LOADED} and {@link FAILED}.
+ */
+ abstract getModel(id: string, routePathSegments: string[], statusWatch?: Array): Observable;
+
+ /**
+ * ** Dispatch GenericAction with provided Type, ComponentState and optionally task.
+ */
+ abstract dispatchAction(type: string, componentState: ComponentState, task?: string): void;
+
+ /**
+ * ** Initialize Service.
+ */
+ abstract initialize(): void;
+}
+
+/**
+ * @inheritDoc
+ */
+@Injectable()
+export class ComponentServiceImpl extends ComponentService {
+ private readonly componentsStateHelper: ComponentsStateHelper;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(private readonly store$: Store,
+ private readonly routerService: RouterService) {
+ super();
+
+ this.componentsStateHelper = new ComponentsStateHelper();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ init(id: string, routeState: RouteState): Observable {
+ this.store$
+ .select((store) => store)
+ .pipe(
+ take(1),
+ map((store) => this._getComponentState(id, routeState.routePathSegments, store.router, store.components))
+ )
+ .subscribe((componentState) => {
+ if (componentState.status === INITIALIZED) {
+ this.store$.dispatch(
+ ComponentInit.of(componentState)
+ );
+ }
+ });
+
+ return this.onInit(id, routeState.routePathSegments);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ idle(componentState: ComponentState): void {
+ this.store$.dispatch(
+ ComponentIdle.of(componentState)
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ load(componentState: ComponentState): Observable {
+ this.routerService
+ .get()
+ .pipe(
+ take(1)
+ )
+ .subscribe((routerState: RouterState) => {
+ if (componentState.status === INITIALIZED) {
+ this.store$.dispatch(
+ ComponentIdle.of(
+ componentState.copy({
+ status: IDLE,
+ navigationId: routerState.navigationId
+ })
+ )
+ );
+ }
+
+ this.store$.dispatch(
+ ComponentLoading.of(
+ componentState.copy({
+ status: LOADING,
+ navigationId: routerState.navigationId
+ })
+ )
+ );
+ });
+
+ return this.onLoaded(componentState.id, componentState.routePathSegments);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ update(componentState: ComponentState): void {
+ this.routerService
+ .get()
+ .pipe(
+ take(1)
+ )
+ .subscribe((routerState) => {
+ this.store$.dispatch(
+ ComponentUpdate.of(
+ componentState.copy({
+ navigationId: routerState.navigationId
+ })
+ )
+ );
+ });
+ }
+
+ /**
+ * @inheritDoc
+ */
+ hasInSegment(id: string, routePathSegments: string[]): Observable {
+ return this.store$
+ .select(STORE_COMPONENTS)
+ .pipe(
+ withLatestFrom(this.routerService.getState()),
+ map(([literalComponentsState, routeState]) =>
+ this._isComponentInStatus(id, routePathSegments, literalComponentsState, routeState, ['*'])),
+ take(1)
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ onInit(id: string, routePathSegments: string[]): Observable {
+ return this.store$
+ .select(STORE_COMPONENTS)
+ .pipe(
+ withLatestFrom(this.routerService.getState()),
+ map(([literalComponentsState, routeState]) =>
+ this._isComponentInStatus(id, routePathSegments, literalComponentsState, routeState, ['*'])),
+ filter((isInitialized) => isInitialized),
+ switchMap(() => this.getModel(id, routePathSegments, ['*'])),
+ take(1)
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ onLoaded(id: string, routePathSegments: string[]): Observable {
+ return this.store$
+ .select(STORE_COMPONENTS)
+ .pipe(
+ withLatestFrom(this.routerService.getState()),
+ map(([literalComponentsState, routeState]) =>
+ this._isComponentInStatus(id, routePathSegments, literalComponentsState, routeState, [LOADED, FAILED])),
+ filter((isLoaded) => isLoaded),
+ switchMap(() => this.getModel(id, routePathSegments)),
+ take(1)
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getModel(id: string, routePathSegments: string[], statusWatch?: Array): Observable {
+ const _statusWatch = statusWatch ?? [LOADED, FAILED];
+
+ return this.store$
+ .select(STORE_COMPONENTS)
+ .pipe(
+ switchMap((literalComponentsState) => this.routerService.get().pipe(
+ map((routerState) => [literalComponentsState, routerState])
+ )),
+ filter(([literalComponentsState, routerState]: [LiteralComponentsState, RouterState]) =>
+ this._isComponentInStatus(id, routePathSegments, literalComponentsState, routerState.state, _statusWatch)),
+ map(([literalComponentsState, routerState]) =>
+ this._createModel(id, routePathSegments, literalComponentsState, routerState))
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ dispatchAction(type: string, componentState: ComponentState, task?: string): void {
+ this.getModel(componentState.id, componentState.routePathSegments, ['*'])
+ .pipe(
+ take(1)
+ )
+ .subscribe((model) =>
+ this.store$.dispatch(
+ GenericAction.of(
+ type,
+ model.getComponentState(),
+ task
+ )
+ )
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ initialize() {
+ // No-op.
+ }
+
+ // Get Component State from Store if exist, otherwise create new State.
+ private _getComponentState(
+ id: string,
+ routePathSegments: string[],
+ routerState: RouterState,
+ literalComponentsState: LiteralComponentsState): ComponentState {
+
+ let _navigationId: number = null;
+ let _routePath: string = null;
+ let _routePathSegments: string[] = [];
+
+ if (routerState) {
+ _navigationId = routerState.navigationId;
+
+ if (routerState.state && !routePathSegments) {
+ _routePath = routerState.state.routePath;
+ _routePathSegments = routerState.state.routePathSegments;
+ }
+ }
+
+ if (routePathSegments) {
+ _routePath = routePathSegments.slice().pop();
+ _routePathSegments = routePathSegments;
+ }
+
+ let componentState = this.componentsStateHelper
+ .setState(literalComponentsState)
+ .getComponentState(
+ id,
+ _routePathSegments
+ );
+
+ if (componentState) {
+ return componentState;
+ }
+
+ componentState = ComponentStateImpl.of({
+ id,
+ status: INITIALIZED,
+ routePath: _routePath,
+ routePathSegments: _routePathSegments,
+ navigationId: _navigationId
+ });
+
+ return componentState;
+ }
+
+ // Utility method that filter if provided state is in desired status.
+ private _isComponentInStatus(
+ id: string,
+ routePathSegments: string[],
+ literalComponentsState: LiteralComponentsState,
+ routeState: RouteState,
+ statusWatch: Array): boolean {
+
+ let _routePathSegments: string[] = [];
+
+ if (CollectionsUtil.isArray(routePathSegments)) {
+ _routePathSegments = routePathSegments;
+ } else if (routeState) {
+ _routePathSegments = routeState.routePathSegments;
+ }
+
+ const componentLiteralState = this.componentsStateHelper
+ .setState(literalComponentsState)
+ .getLiteralComponentState(
+ id,
+ _routePathSegments
+ );
+
+ if (!componentLiteralState) {
+ return false;
+ }
+
+ if (statusWatch.indexOf('*') !== -1) {
+ return true;
+ }
+
+ return statusWatch.indexOf(componentLiteralState.status) !== -1;
+ }
+
+ // Creates Model from provided data.
+ private _createModel(
+ id: string,
+ routePathSegments: string[],
+ literalComponentsState: LiteralComponentsState,
+ routerState: RouterState): ComponentModel {
+
+ let _routePathSegments: string[] = [];
+
+ if (CollectionsUtil.isArray(routePathSegments)) {
+ _routePathSegments = routePathSegments;
+ } else if (routerState && routerState.state) {
+ _routePathSegments = routerState.state.routePathSegments;
+ }
+
+ const componentState = this.componentsStateHelper
+ .setState(literalComponentsState)
+ .getComponentState(
+ id,
+ _routePathSegments
+ );
+
+ return ComponentModel.of(componentState, CollectionsUtil.cloneDeep(routerState));
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/index.ts
new file mode 100644
index 0000000000..fab0d2dcd2
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/services/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component.service';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.spec.ts
new file mode 100644
index 0000000000..36ca688537
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.spec.ts
@@ -0,0 +1,293 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import {
+ COMPONENT_CLEAR_DATA,
+ COMPONENT_FAILED,
+ COMPONENT_IDLE,
+ COMPONENT_INIT,
+ COMPONENT_LOADED,
+ COMPONENT_LOADING,
+ COMPONENT_UPDATE,
+ ComponentClearData,
+ ComponentFailed,
+ ComponentIdle,
+ ComponentInit,
+ ComponentLoaded,
+ ComponentLoading,
+ ComponentUpdate
+} from './component.actions';
+import { ComponentStateImpl } from '../../model';
+import { BaseAction, BaseActionWithPayload } from '../../../ngrx';
+
+describe('ComponentActions', () => {
+ describe('ComponentInit', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentInit(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentInit(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_INIT);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentInit(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentInit.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentInit);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentIdle', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentIdle(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentIdle(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_IDLE);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentIdle(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentIdle.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentIdle);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentLoading', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentLoading(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentLoading(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_LOADING);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentLoading(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentLoading.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentLoading);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentLoaded', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentLoaded(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentLoaded(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_LOADED);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentLoaded(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentLoaded.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentLoaded);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentFailed', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentFailed(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentFailed(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_FAILED);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentFailed(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentFailed.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentFailed);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentUpdate', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentUpdate(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentUpdate(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_UPDATE);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentUpdate(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentUpdate.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentUpdate);
+ });
+ });
+ });
+ });
+ });
+
+ describe('ComponentClearData', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new ComponentClearData(ComponentStateImpl.of({}))).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new ComponentClearData(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance.type).toEqual(COMPONENT_CLEAR_DATA);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new ComponentClearData(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = ComponentClearData.of(ComponentStateImpl.of({}));
+
+ // Then
+ expect(instance).toBeInstanceOf(ComponentClearData);
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.ts
new file mode 100644
index 0000000000..467aca13e0
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/component.actions.ts
@@ -0,0 +1,177 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { BaseActionWithPayload } from '../../../ngrx/actions';
+
+import { ComponentState } from '../../model';
+
+/**
+ * ** Action Identifier for Component Initialization.
+ */
+export const COMPONENT_INIT = '[component] Init';
+
+/**
+ * ** Action Identifier for Component Idle.
+ */
+export const COMPONENT_IDLE = '[component] Idle';
+
+/**
+ * ** Action Identifier for Component start Loading data.
+ */
+export const COMPONENT_LOADING = '[component] Loading';
+
+/**
+ * ** Action Identifier for Component Loaded data.
+ */
+export const COMPONENT_LOADED = '[component] Loaded';
+
+/**
+ * ** Action Identifier for Component Failed loading data.
+ */
+export const COMPONENT_FAILED = '[component] Failed';
+
+/**
+ * ** Action Identifier for Component Update state.
+ */
+export const COMPONENT_UPDATE = '[component] Update';
+
+/**
+ * ** Action Identifier for Component Clear data.
+ */
+export const COMPONENT_CLEAR_DATA = '[component] Clear data';
+
+/**
+ * ** Action for Component Initialization.
+ *
+ *
+ */
+export class ComponentInit extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_INIT, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentInit(payload);
+ }
+}
+
+/**
+ * ** Action for Component Idle.
+ *
+ *
+ */
+export class ComponentIdle extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_IDLE, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentIdle(payload);
+ }
+}
+
+/**
+ * ** Action for Component Loading.
+ *
+ *
+ */
+export class ComponentLoading extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_LOADING, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentLoading(payload);
+ }
+}
+
+/**
+ * ** Action for Component Loaded.
+ *
+ *
+ */
+export class ComponentLoaded extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_LOADED, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentLoaded(payload);
+ }
+}
+
+/**
+ * ** Action for Component Failed.
+ *
+ *
+ */
+export class ComponentFailed extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_FAILED, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentFailed(payload);
+ }
+}
+
+/**
+ * ** Action for Component Update.
+ *
+ *
+ */
+export class ComponentUpdate extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_UPDATE, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentUpdate(payload);
+ }
+}
+
+/**
+ * ** Action for Component Clear Data.
+ *
+ *
+ */
+export class ComponentClearData extends BaseActionWithPayload {
+ constructor(payload: ComponentState) {
+ super(COMPONENT_CLEAR_DATA, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: ComponentState) {
+ return new ComponentClearData(payload);
+ }
+}
+
+/**
+ * ** Union of all Actions that could be use as a type in Effects/Reducers/etc...
+ */
+export type ComponentActions = ComponentInit | ComponentIdle | ComponentLoading |
+ ComponentLoaded | ComponentFailed | ComponentUpdate | ComponentClearData;
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/index.ts
new file mode 100644
index 0000000000..f52a972a87
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/actions/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component.actions';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/index.ts
new file mode 100644
index 0000000000..22087bff61
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './actions';
+export * from './reducers';
+export * from './operators';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.spec.ts
new file mode 100644
index 0000000000..dba55fb8fd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.spec.ts
@@ -0,0 +1,234 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { throwError } from 'rxjs';
+
+import { marbles } from 'rxjs-marbles/jasmine';
+
+import { RouterState, RouteSegments, RouteState } from '../../../router';
+
+import { BaseActionWithPayload } from '../../../ngrx';
+
+import { ComponentModel, ComponentState, ComponentStateImpl, FAILED, INITIALIZED, LOADED } from '../../model';
+import { ComponentService } from '../../services';
+
+import { ComponentFailed } from '../actions';
+
+import { getModel, getModelAndTask, handleActionError } from './component-rx.operators';
+import { CollectionsUtil } from '../../../../utils';
+
+describe('rxjs operators', () => {
+ describe('|getModel|', () => {
+ it('should verify will create ComponentModel down the stream', marbles((m) => {
+ // Given
+ const inputActionStream$ = m.cold('----a---b----', {
+ a: new ActionStub(ComponentStateImpl.of({
+ id: 'test1',
+ status: INITIALIZED,
+ routePathSegments: ['entity/10']
+ })),
+ b: new ActionStub(ComponentStateImpl.of({
+ id: 'test2', status: LOADED,
+ routePathSegments: ['entity/225']
+ }))
+ });
+ const getModelStream1$ = m.cold('-a', {
+ a: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test1', status: INITIALIZED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/10'),
+ 'entity/5'
+ ),
+ 3
+ )
+ )
+ });
+ const getModelStream2$ = m.cold('--a', {
+ a: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test5', status: LOADED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/225'),
+ 'entity/225'
+ ),
+ 3
+ )
+ )
+ });
+ const expectedOutputStream$ = m.cold('-----a----b----', {
+ a: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test1', status: INITIALIZED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/10'),
+ 'entity/5'
+ ),
+ 3
+ )
+ ),
+ b: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test5', status: LOADED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/225'),
+ 'entity/225'
+ ),
+ 3
+ )
+ )
+ });
+
+ const componentServiceStub = jasmine.createSpyObj('componentService', ['getModel']);
+ let cnt = 0;
+ componentServiceStub.getModel.and.callFake(() => {
+ cnt++;
+ if (cnt === 1) {
+ return getModelStream1$;
+ } else {
+ return getModelStream2$;
+ }
+ });
+
+ // When
+ const response$ = getModel(componentServiceStub)(inputActionStream$);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedOutputStream$);
+ }));
+ });
+
+ describe('|getModelAndTask|', () => {
+ it('should verify will create ComponentModel down the stream', marbles((m) => {
+ // Given
+ const taskIdentifier1 = `patch_entity __ ${ new Date().toISOString() }`;
+ const taskIdentifier2 = `patch_dataset __ ${ new Date().toISOString() }`;
+ spyOn(CollectionsUtil, 'interpolateString').and.returnValues(taskIdentifier1, taskIdentifier2);
+ const inputActionStream$ = m.cold('----a---b----', {
+ a: new ActionStub(
+ ComponentStateImpl.of({
+ id: 'test1',
+ status: INITIALIZED,
+ routePathSegments: ['entity/10']
+ }),
+ taskIdentifier1
+ ),
+ b: new ActionStub(
+ ComponentStateImpl.of({
+ id: 'test2', status: LOADED,
+ routePathSegments: ['entity/225']
+ }),
+ taskIdentifier2
+ )
+ });
+ const getModelStream1$ = m.cold('-a', {
+ a: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test1', status: INITIALIZED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/10'),
+ 'entity/5'
+ ),
+ 3
+ )
+ )
+ });
+ const getModelStream2$ = m.cold('--a', {
+ a: ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test5', status: LOADED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/225'),
+ 'entity/225'
+ ),
+ 3
+ )
+ )
+ });
+ const expectedOutputStream$ = m.cold('-----a----b----', {
+ a: [
+ ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test1', status: INITIALIZED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/10'),
+ 'entity/5'
+ ),
+ 3
+ )
+ ),
+ taskIdentifier1
+ ] as [ComponentModel, string],
+ b: [
+ ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test5', status: LOADED }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/225'),
+ 'entity/225'
+ ),
+ 3
+ )
+ ),
+ taskIdentifier2
+ ] as [ComponentModel, string]
+ });
+
+ const componentServiceStub = jasmine.createSpyObj('componentService', ['getModel']);
+ let cnt = 0;
+ componentServiceStub.getModel.and.callFake(() => {
+ cnt++;
+ if (cnt === 1) {
+ return getModelStream1$;
+ } else {
+ return getModelStream2$;
+ }
+ });
+
+ // When
+ const response$ = getModelAndTask(componentServiceStub)(inputActionStream$);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedOutputStream$);
+ }));
+ });
+
+ describe('|handleActionError|', () => {
+ it('should verify will handle Stream error and return ComponentFailed action', marbles((m) => {
+ // Given
+ const inputModel = ComponentModel.of(
+ ComponentStateImpl.of({ id: 'test1', status: INITIALIZED, navigationId: 3 }),
+ RouterState.of(
+ RouteState.of(
+ RouteSegments.of('entity/10'),
+ 'entity/5'
+ ),
+ 3
+ )
+ );
+ const error = new Error('Http Failure');
+ const getResponseStream$ = throwError(error);
+ const expectedOutputStream$ = m.cold('(a|)', {
+ a: ComponentFailed.of(
+ ComponentStateImpl.of({ id: 'test1', status: FAILED, navigationId: 3, error })
+ )
+ });
+
+ // When
+ const response$ = handleActionError(inputModel)(getResponseStream$);
+
+ // Then
+ m.expect(response$).toBeObservable(expectedOutputStream$);
+ }));
+ });
+});
+
+class ActionStub extends BaseActionWithPayload {
+ constructor(payload: ComponentState, task?: string) {
+ super('[component] action stub', payload, task);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.ts
new file mode 100644
index 0000000000..d9d9374ef8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/component-rx.operators.ts
@@ -0,0 +1,99 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { iif, Observable, of, throwError } from 'rxjs';
+import { catchError, map, mergeMap, switchMap, take } from 'rxjs/operators';
+
+import { BaseActionWithPayload } from '../../../ngrx/actions';
+
+import { ComponentService } from '../../services';
+import { ComponentModel, ComponentState } from '../../model';
+
+import { ComponentFailed } from '../actions';
+
+/**
+ * ** RXJS Operator that fetch Component Model from Store using provided ComponentService.
+ *
+ * - Returns Observable
+ *
+ *
+ */
+export const getModel = (componentService: ComponentService) =>
+ (source$: Observable>): Observable =>
+ source$.pipe(
+ extractAction,
+ switchMap((action) => subscribeForModel(action, componentService))
+ );
+
+/**
+ * ** RXJS Operator that fetch Component Model from Store using provided ComponentService and consume task from Action.
+ *
+ * - Returns Observable<[ComponentModel, string]>
+ *
+ *
+ */
+export const getModelAndTask = (componentService: ComponentService) =>
+ (source$: Observable>): Observable<[ComponentModel, string]> =>
+ source$.pipe(
+ extractAction,
+ switchMap((action) =>
+ subscribeForModel(action, componentService).pipe(
+ map((model) => [model, action.task] as [ComponentModel, string])
+ )
+ )
+ );
+
+/**
+ * ** Handle Error from some Action, ideal to use for API response.
+ *
+ * - Catch error if happen, append it to Model, set status to FAILED,
+ * create instance of {@link ComponentFailed} and return stream in success state using {@link of} rjxs operator.
+ *
+ *
+ */
+export const handleActionError = (model: ComponentModel) =>
+ (source$: Observable>): Observable> =>
+ source$.pipe(
+ catchError((error: unknown) => of(
+ ComponentFailed.of(
+ model
+ .withError(error as Error)
+ .withStatusFailed()
+ .getComponentState()
+ )
+ ))
+ );
+
+/**
+ * ** Validate if Action is instance of {@link BaseActionWithPayload} and pass down the stream.
+ *
+ *
+ */
+const extractAction = (source$: Observable>) =>
+ source$.pipe(
+ mergeMap((action) =>
+ iif(
+ () => action instanceof BaseActionWithPayload,
+ of(action),
+ throwError(
+ new Error('Unsupported Action type. It should be subclass of BaseActionWithPayload')
+ )
+ )
+ )
+ );
+
+/**
+ * ** Make actual subscription to Store for ComponentModel.
+ *
+ *
+ */
+const subscribeForModel = (action: BaseActionWithPayload, componentService: ComponentService) =>
+ componentService
+ .getModel(action.payload.id, action.payload.routePathSegments, ['*'])
+ .pipe(
+ take(1)
+ );
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/index.ts
new file mode 100644
index 0000000000..ec099b75ac
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/operators/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component-rx.operators';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.spec.ts
new file mode 100644
index 0000000000..801b50a8b3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.spec.ts
@@ -0,0 +1,425 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable arrow-body-style,prefer-arrow/prefer-arrow-functions */
+
+import { ROUTER_NAVIGATION, RouterNavigationPayload } from '@ngrx/router-store';
+
+import { CallFake } from '../../../../unit-testing';
+
+import { CollectionsUtil } from '../../../../utils';
+
+import { BaseActionWithPayload } from '../../../ngrx';
+
+import { RouteSegments, RouteState } from '../../../router';
+
+import {
+ ComponentsStateHelper,
+ ComponentState,
+ FAILED,
+ IDLE,
+ INITIALIZED,
+ LiteralComponentsState,
+ LiteralComponentState,
+ LOADED,
+ LOADING,
+ StatusType
+} from '../../model';
+
+import {
+ COMPONENT_CLEAR_DATA,
+ COMPONENT_FAILED,
+ COMPONENT_INIT,
+ COMPONENT_LOADING,
+ COMPONENT_UPDATE,
+ ComponentIdle,
+ ComponentLoaded
+} from '../actions';
+
+import { componentReducer } from './component.reducer';
+
+describe('componentReducer', () => {
+ let id: string;
+ let routePathSegments: string[];
+ let literalComponentsState: LiteralComponentsState;
+ let updateComponentHelperSpy: jasmine.Spy;
+ let getComponentHelperSpy: jasmine.Spy;
+ let getStateHelperSpy: jasmine.Spy;
+
+ beforeEach(() => {
+ id = 'testComponent1';
+ routePathSegments = ['test_entity/15'];
+ literalComponentsState = {
+ components: {},
+ routePathSegments: {}
+ };
+ updateComponentHelperSpy = spyOn(ComponentsStateHelper.prototype, 'updateLiteralComponentState').and.callFake(CallFake);
+ getComponentHelperSpy = spyOn(ComponentsStateHelper.prototype, 'getLiteralComponentState');
+ getStateHelperSpy = spyOn(ComponentsStateHelper.prototype, 'getState');
+ });
+
+ describe('Actions::', () => {
+ describe('|COMPONENT_INIT|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const action = new ActionStub(COMPONENT_INIT, createComponentState(null));
+ getStateHelperSpy
+ .and
+ .returnValue(createComponentsState(createComponentState(INITIALIZED).toLiteral()));
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(result).toEqual(createComponentsState(createComponentState(INITIALIZED).toLiteral()));
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(createComponentState(INITIALIZED).toLiteral());
+ });
+ });
+
+ describe('|COMPONENT_IDLE|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const action = ComponentIdle.of(createComponentState(INITIALIZED, 'test-team', 4));
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(INITIALIZED).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(createComponentsState(createComponentState(IDLE, 'test-team', 4).toLiteral()));
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(createComponentsState(createComponentState(IDLE, 'test-team', 4).toLiteral()));
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(createComponentState(IDLE, 'test-team', 4).toLiteral());
+ });
+ });
+
+ describe('|COMPONENT_LOADING|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const action = new ActionStub(COMPONENT_LOADING, createComponentState(IDLE, 'test-team', 4));
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(IDLE).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(createComponentsState(createComponentState(LOADING, 'test-team', 4).toLiteral()));
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(createComponentsState(createComponentState(LOADING, 'test-team', 4).toLiteral()));
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(createComponentState(LOADING, 'test-team', 4).toLiteral());
+ });
+ });
+
+ describe('|COMPONENT_UPDATE|', () => {
+ it('should verify invokes correct methods (data from action)', () => {
+ // Given
+ const data = new Map([['users', []], ['cities', {}]]);
+ const action = new ActionStub(
+ COMPONENT_UPDATE,
+ createComponentState(IDLE, 'test-team', 7, data)
+ );
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(LOADING).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(IDLE, 'test-team', 7, data).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(IDLE, 'test-team', 7, data).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(IDLE, 'test-team', 7, data).toLiteral()
+ );
+ });
+
+ it('should verify invokes correct methods (data from store)', () => {
+ // Given
+ const data = new Map([['countries', []], ['payload', {}]]);
+ const action = new ActionStub(
+ COMPONENT_UPDATE,
+ createComponentState(FAILED, 'test-team', 6)
+ );
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(LOADING, 'test-team', 4, data).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 6, data).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 6, data).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(FAILED, 'test-team', 6, data).toLiteral()
+ );
+ });
+ });
+
+ describe('|COMPONENT_LOADED|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const action = ComponentLoaded.of(createComponentState(LOADING, 'test-team', 11));
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(IDLE, 'test-team', 7).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(LOADED, 'test-team', 11).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(LOADED, 'test-team', 11).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(LOADED, 'test-team', 11).toLiteral()
+ );
+ });
+ });
+
+ describe('|COMPONENT_FAILED|', () => {
+ it('should verify invokes correct methods (data from action)', () => {
+ // Given
+ const data = new Map([['data', []], ['invokes', {}]]);
+ const action = new ActionStub(
+ COMPONENT_FAILED,
+ createComponentState(IDLE, 'test-team', 9, data)
+ );
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(LOADING).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 9, data).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 9, data).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(FAILED, 'test-team', 9, data).toLiteral()
+ );
+ });
+
+ it('should verify invokes correct methods (data from store)', () => {
+ // Given
+ const data = new Map([['wifi', []], ['routers', {}]]);
+ const action = new ActionStub(
+ COMPONENT_FAILED,
+ createComponentState(LOADING, 'test-team', 16)
+ );
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(LOADING, 'test-team', 14, data).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 16, data).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(FAILED, 'test-team', 16, data).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(FAILED, 'test-team', 16, data).toLiteral()
+ );
+ });
+ });
+
+ describe('|COMPONENT_CLEAR_DATA|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const data = new Map([['admins', []], ['telcos', {}]]);
+ const action = new ActionStub(
+ COMPONENT_CLEAR_DATA,
+ createComponentState(LOADED, 'test-team', 21)
+ );
+ getComponentHelperSpy
+ .and
+ .returnValue(createComponentState(LOADED, 'test-team', 15, data).toLiteral());
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(LOADING, 'test-team', 21).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(getComponentHelperSpy).toHaveBeenCalledWith(id, routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(LOADING, 'test-team', 21).toLiteral()
+ )
+ );
+ expect(updateComponentHelperSpy).toHaveBeenCalledWith(
+ createComponentState(LOADING, 'test-team', 21).toLiteral()
+ );
+ });
+ });
+
+ describe('|ROUTER_NAVIGATION|', () => {
+ it('should verify invokes correct methods', () => {
+ // Given
+ const routerNavigationPayload = createRouterNavigationPayload();
+ const action = new ActionStub>(
+ ROUTER_NAVIGATION,
+ routerNavigationPayload
+ );
+ const resetComponentsHelperSpy = spyOn(ComponentsStateHelper.prototype, 'resetComponentStates').and.callFake(CallFake);
+ getStateHelperSpy
+ .and
+ .returnValue(
+ createComponentsState(
+ createComponentState(IDLE, 'test-team', 17).toLiteral()
+ )
+ );
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(resetComponentsHelperSpy).toHaveBeenCalledWith(routerNavigationPayload.routerState.routePathSegments);
+ expect(result).toEqual(
+ createComponentsState(
+ createComponentState(IDLE, 'test-team', 17).toLiteral()
+ )
+ );
+ });
+ });
+
+ describe('|UNKNOWN_ACTION for this Reducer|', () => {
+ it('should verify will return state as it is', () => {
+ // Given
+ const action = new ActionStub('[action] unknown type', {});
+
+ // When
+ const result = componentReducer(literalComponentsState, action);
+
+ // Then
+ expect(result).toBe(literalComponentsState);
+ });
+ });
+ });
+});
+
+class ActionStub extends BaseActionWithPayload {
+ constructor(type: string, payload: T) {
+ super(type, payload);
+ }
+}
+
+const createComponentState = (
+ status: StatusType,
+ search = '',
+ navigationId = 3,
+ data: Map = new Map()): ComponentState => {
+
+ const literalComponentState: LiteralComponentState = {
+ id: 'testComponent1',
+ status,
+ routePath: 'test_entity/15',
+ routePathSegments: ['test_entity/15'],
+ navigationId,
+ search,
+ data: CollectionsUtil.transformMapToObject(data)
+ };
+
+ return {
+ ...literalComponentState as any,
+ data,
+ copy: CallFake,
+ toLiteral: () => literalComponentState,
+ toLiteralDeepClone: () => literalComponentState
+ } as ComponentState;
+};
+
+const createComponentsState = (componentState: LiteralComponentState): LiteralComponentsState => {
+ return {
+ components: {},
+ routePathSegments: {
+ 'test_entity/15': {
+ components: {
+ testComponent1: componentState
+ },
+ routePathSegments: {}
+ }
+ }
+ };
+};
+
+const createRouterNavigationPayload = (): RouterNavigationPayload => {
+ return {
+ routerState: RouteState.of(
+ RouteSegments.of('entity/21'),
+ 'entity/21'
+ ),
+ event: null
+ };
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.ts
new file mode 100644
index 0000000000..bdb6ec45de
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/component.reducer.ts
@@ -0,0 +1,155 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable arrow-body-style,prefer-arrow/prefer-arrow-functions */
+
+import { ROUTER_NAVIGATION, RouterNavigationAction } from '@ngrx/router-store';
+
+import { CollectionsUtil } from '../../../../utils';
+
+import { NavigationActions, RouteSegments, RouteState } from '../../../router';
+
+import { ComponentsStateHelper, ComponentState, FAILED, IDLE, INITIALIZED, LiteralComponentState, LOADED, LOADING } from '../../model';
+
+import {
+ COMPONENT_CLEAR_DATA,
+ COMPONENT_FAILED,
+ COMPONENT_IDLE,
+ COMPONENT_INIT,
+ COMPONENT_LOADED,
+ COMPONENT_LOADING,
+ COMPONENT_UPDATE,
+ ComponentActions,
+ ComponentClearData,
+ ComponentIdle,
+ ComponentInit,
+ ComponentLoaded,
+ ComponentLoading,
+ ComponentUpdate
+} from '../actions';
+
+const stateHelper = new ComponentsStateHelper();
+
+/**
+ * ** Reducer for Components Actions.
+ *
+ *
+ */
+export function componentReducer(
+ state = stateHelper.getState(),
+ action: ComponentActions | NavigationActions = { type: null, payload: null }) {
+
+ let actionComponentState: ComponentState;
+ let actionLiteralComponentState: LiteralComponentState;
+ let storeLiteralComponentState: LiteralComponentState;
+
+ stateHelper.setState(state);
+
+ switch (action.type) {
+ case COMPONENT_INIT:
+ actionComponentState = (action as ComponentInit).payload;
+
+ stateHelper.updateLiteralComponentState({
+ ...actionComponentState.toLiteralDeepClone(),
+ status: INITIALIZED
+ });
+
+ return stateHelper.getState();
+ case COMPONENT_IDLE:
+ case COMPONENT_LOADING:
+ actionComponentState = (action as ComponentIdle | ComponentLoading).payload;
+ storeLiteralComponentState = stateHelper
+ .getLiteralComponentState(actionComponentState.id, actionComponentState.routePathSegments);
+
+ actionLiteralComponentState = actionComponentState.toLiteralDeepClone();
+
+ stateHelper.updateLiteralComponentState({
+ ...storeLiteralComponentState,
+ ...actionLiteralComponentState,
+ status: action instanceof ComponentIdle
+ ? IDLE
+ : LOADING
+ });
+
+ return stateHelper.getState();
+ case COMPONENT_UPDATE:
+ case COMPONENT_LOADED:
+ actionComponentState = (action as (ComponentLoaded | ComponentUpdate)).payload;
+ storeLiteralComponentState = stateHelper
+ .getLiteralComponentState(actionComponentState.id, actionComponentState.routePathSegments);
+
+ actionLiteralComponentState = actionComponentState.toLiteralDeepClone();
+
+ stateHelper.updateLiteralComponentState({
+ ...storeLiteralComponentState,
+ ...actionLiteralComponentState,
+ status: action instanceof ComponentLoaded
+ ? LOADED
+ : actionComponentState.status,
+ data: getComponentStateData(actionLiteralComponentState, storeLiteralComponentState)
+ });
+
+ return stateHelper.getState();
+ case COMPONENT_FAILED:
+ actionComponentState = (action as (ComponentLoaded | ComponentUpdate)).payload;
+ storeLiteralComponentState = stateHelper
+ .getLiteralComponentState(actionComponentState.id, actionComponentState.routePathSegments);
+
+ actionLiteralComponentState = actionComponentState.toLiteralDeepClone();
+
+ stateHelper.updateLiteralComponentState({
+ ...storeLiteralComponentState,
+ ...actionLiteralComponentState,
+ data: getComponentStateData(actionLiteralComponentState, storeLiteralComponentState),
+ status: FAILED
+ });
+
+ return stateHelper.getState();
+ case COMPONENT_CLEAR_DATA:
+ actionComponentState = (action as ComponentClearData).payload;
+ storeLiteralComponentState = stateHelper.getLiteralComponentState(
+ actionComponentState.id,
+ actionComponentState.routePathSegments);
+
+ stateHelper.updateLiteralComponentState({
+ ...storeLiteralComponentState,
+ ...actionComponentState.toLiteralDeepClone(),
+ data: {},
+ status: LOADING
+ });
+
+ return stateHelper.getState();
+ case ROUTER_NAVIGATION:
+ const routeSegments = (action as RouterNavigationAction).payload.routerState.routeSegments;
+
+ if (!(routeSegments instanceof RouteSegments)) {
+ return state;
+ }
+
+ stateHelper.resetComponentStates(routeSegments.routePathSegments);
+
+ return stateHelper.getState();
+ default:
+ return state;
+ }
+}
+
+const getComponentStateData = (
+ actionComponentState: LiteralComponentState,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ storeLiteralComponentState: LiteralComponentState): { [key: string]: any } => {
+
+ if (CollectionsUtil.isLiteralObjectWithProperties(actionComponentState.data)) {
+ return actionComponentState.data;
+ }
+
+ if (CollectionsUtil.isDefined(storeLiteralComponentState)) {
+ return storeLiteralComponentState.data;
+ }
+
+ return {};
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/index.ts
new file mode 100644
index 0000000000..56ef825780
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/component/state/reducers/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component.reducer';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/index.ts
new file mode 100644
index 0000000000..f76250a9ae
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/index.ts
@@ -0,0 +1,13 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component';
+export * from './navigation';
+export * from './ngrx';
+export * from './router';
+export * from './system-events';
+export * from './url-state-manager';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/index.ts
new file mode 100644
index 0000000000..4117a214b3
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './services';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/public-api.ts
new file mode 100644
index 0000000000..61c133a9d8
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export { NavigationService } from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/index.ts
new file mode 100644
index 0000000000..c438c6ff08
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './navigation.service';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.spec.ts
new file mode 100644
index 0000000000..bff88152f1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.spec.ts
@@ -0,0 +1,443 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation */
+
+import { NavigationExtras, Router } from '@angular/router';
+import { TestBed } from '@angular/core/testing';
+
+import { of } from 'rxjs';
+
+import { TaurusRouteData } from '../../../common';
+
+import { RouterService, RouteSegments, RouteState } from '../../router';
+
+import { NavigationService } from './navigation.service';
+
+describe('NavigationService', () => {
+ let routerStub: jasmine.SpyObj;
+ let routerServiceStub: jasmine.SpyObj;
+
+ let service: NavigationService;
+
+ beforeEach(() => {
+ routerStub = jasmine.createSpyObj('router', ['navigate']);
+ routerServiceStub = jasmine.createSpyObj('routerService', ['getState', 'initialize']);
+
+ TestBed.configureTestingModule({
+ providers: [
+ { provide: Router, useValue: routerStub },
+ { provide: RouterService, useValue: routerServiceStub },
+ NavigationService
+ ]
+ });
+
+ service = TestBed.inject(NavigationService);
+
+ // @ts-ignore
+ service['router'] = routerStub;
+ // @ts-ignore
+ service['routerService'] = routerServiceStub;
+ });
+
+ it('should verify instance is created', () => {
+ // Then
+ expect(service).toBeDefined();
+ });
+
+ describe('Methods::()', () => {
+ describe('|initialize|', () => {
+ it('should verify method exist and is callable', () => {
+ // Then
+ expect(service.initialize).toBeDefined();
+ expect(() => service.initialize()).not.toThrow();
+ expect(routerServiceStub.initialize).toHaveBeenCalled();
+ });
+ });
+
+ describe('|navigate|', () => {
+ let url: string;
+ let urlChunks: string[];
+ let extras: NavigationExtras;
+
+ beforeEach(() => {
+ url = '/explore/data-jobs';
+ urlChunks = ['explore', 'data-jobs'];
+ extras = {
+ queryParams: {
+ search: 'vdk'
+ },
+ replaceUrl: true,
+ queryParamsHandling: 'merge'
+ };
+ // @ts-ignore
+ service['router'] = routerStub;
+ });
+
+ it('should verify will call Router.navigate() from Angular', () => {
+ // Given
+ routerStub.navigate.and.returnValue(Promise.resolve(true));
+
+ // When
+ service.navigate(url);
+
+ // Then
+ expect(routerStub.navigate).toHaveBeenCalledWith(['/explore', 'data-jobs']);
+ });
+
+ it('should verify will return Promise', () => {
+ // Given
+ routerStub.navigate.and.returnValue(Promise.resolve(true));
+
+ // When
+ const returnedValue = service.navigate(url);
+
+ // Then
+ expect(returnedValue).toBeInstanceOf(Promise);
+ });
+
+ it('should verify will return Promise.resolve(false) on Nil value for url', () => {
+ // When
+ const returnedValue = service.navigate(null);
+
+ // Then
+ returnedValue.then((v) => expect(v).toBeFalse());
+ expect(routerStub.navigate).not.toHaveBeenCalled();
+ });
+
+ it('should verify will handle both url string and url Array', () => {
+ // Given
+ routerStub.navigate.and.returnValue(Promise.resolve(true));
+
+ // When
+ service.navigate(url);
+ service.navigate(urlChunks);
+
+ // Then
+ expect(routerStub.navigate).toHaveBeenCalledTimes(2);
+ expect(routerStub.navigate.calls.argsFor(0)).toEqual([['/explore', 'data-jobs']]);
+ expect(routerStub.navigate.calls.argsFor(1)).toEqual([['/explore', 'data-jobs']]);
+ });
+
+ it('should verify will handle extras parameters', () => {
+ // Given
+ routerStub.navigate.and.returnValue(Promise.resolve(true));
+
+ // When
+ service.navigate(url, extras);
+
+ // Then
+ expect(routerStub.navigate).toHaveBeenCalledWith(['/explore', 'data-jobs'], extras);
+ });
+ });
+
+ describe('|_navigationSystemEventHandler_|', () => {
+ it('should verify will skip navigation if payload is Nil', () => {
+ // Given
+ const payload: { url: string | string[]; extras?: NavigationExtras } = null;
+ const navigateSpy = spyOn(service, 'navigate').and.callFake(() => Promise.resolve(false));
+
+ // When
+ const returnedValue = service._navigationSystemEventHandler_(payload);
+
+ // Then
+ returnedValue.then((v) => expect(v).toBeFalse());
+ expect(navigateSpy).not.toHaveBeenCalled();
+ });
+
+ it('should verify will execute navigation when payload is provided', () => {
+ // Given
+ const payload = { url: '/explore/data-jobs', extras: { queryParams: { search: 'vdk' } } };
+ spyOn(service, 'navigate').and.callFake(() => Promise.resolve(true));
+
+ // When
+ const returnedValue = service._navigationSystemEventHandler_(payload);
+
+ // Then
+ returnedValue.then((v) => expect(v).toBeTrue());
+ expect(service.navigate).toHaveBeenCalledWith(payload.url, payload.extras);
+ });
+ });
+
+ describe('|navigateBack|', () => {
+ it('should verify will invoke correct methods when NavigationAction provided', (done) => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ {
+ navigateBack: {
+ path: '$.parent',
+ queryParamsHandling: 'preserve',
+ queryParams: { randomParam: 'abc' }
+ }
+ } as TaurusRouteData,
+ {},
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'entity/25',
+ {
+ navigateTo: {
+ path: '/domain/context/entity/{0}/update/information',
+ replacers: [
+ { searchValue: '{0}', replaceValue: '$.entity' }
+ ],
+ queryParams: { showWizard: true }
+ }
+ } as TaurusRouteData,
+ { entityId: 25 },
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'team-test' },
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId'
+ ),
+ 'delivery'
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ routerServiceStub.getState.and.returnValue(of(routeState));
+ const navigateSpy = spyOn(service, 'navigate').and.returnValue(Promise.resolve(true));
+
+ // When
+ const promise = service.navigateBack();
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ promise.then((value) => {
+ expect(value).toBeTrue();
+ expect(navigateSpy).toHaveBeenCalledWith(
+ '/domain/context/entity/25',
+ {
+ queryParamsHandling: 'preserve'
+ }
+ );
+
+ done();
+ });
+ });
+
+ it('should verify will invoke correct methods when no NavigationAction provided', (done) => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ {},
+ {},
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'entity/25',
+ {},
+ { entityId: 25 },
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'team-test' },
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId'
+ ),
+ 'delivery'
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ routerServiceStub.getState.and.returnValue(of(routeState));
+ const navigateSpy = spyOn(service, 'navigate').and.returnValue(Promise.resolve(true));
+
+ // When
+ const promise = service.navigateBack();
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ promise.then((value) => {
+ expect(value).toBeTrue();
+ expect(navigateSpy).toHaveBeenCalledWith(
+ '/domain/context/entity/25',
+ {
+ queryParamsHandling: 'merge'
+ }
+ );
+
+ done();
+ });
+ });
+ });
+
+ describe('|navigateTo|', () => {
+ it('should verify will invoke correct methods preserve routerState QueryParams', (done) => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ {
+ navigateBack: {
+ path: '$.parent',
+ queryParamsHandling: 'preserve',
+ queryParams: { randomParam: 'abc' }
+ }
+ } as TaurusRouteData,
+ {},
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'entity/25',
+ {
+ navigateTo: {
+ path: '/domain/context/entity/{0}/update/information',
+ replacers: [
+ { searchValue: '{0}', replaceValue: '$.entity' }
+ ],
+ queryParamsHandling: 'preserve',
+ queryParams: { showWizard: true }
+ }
+ } as TaurusRouteData,
+ { entityId: 25 },
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'team-test' },
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId'
+ ),
+ 'delivery'
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ routerServiceStub.getState.and.returnValue(of(routeState));
+ const navigateSpy = spyOn(service, 'navigate').and.returnValue(Promise.resolve(true));
+
+ // When
+ const promise = service.navigateTo({ '$.entity': '101' });
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ promise.then((value) => {
+ expect(value).toBeTrue();
+ expect(navigateSpy).toHaveBeenCalledWith(
+ '/domain/context/entity/101/update/information',
+ {
+ queryParamsHandling: 'preserve'
+ }
+ );
+
+ done();
+ });
+ });
+
+ it('should verify will invoke correct methods merge QueryParams, provided and from routeState', (done) => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ {
+ navigateBack: {
+ path: '$.parent',
+ queryParamsHandling: 'preserve',
+ queryParams: { randomParam: 'abc' }
+ }
+ } as TaurusRouteData,
+ {},
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'entity/25',
+ {
+ navigateTo: {
+ path: '/domain/context/entity/{0}/update/information',
+ replacers: [
+ { searchValue: '{0}', replaceValue: '$.entity' }
+ ],
+ queryParams: { showWizard: true },
+ queryParamsHandling: 'merge'
+ }
+ } as TaurusRouteData,
+ { entityId: 25 },
+ { search: 'team-test' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'team-test' },
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId'
+ ),
+ 'delivery'
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ routerServiceStub.getState.and.returnValue(of(routeState));
+ const navigateSpy = spyOn(service, 'navigate').and.returnValue(Promise.resolve(true));
+
+ // When
+ const promise = service.navigateTo({ '$.entity': '101' });
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ promise.then((value) => {
+ expect(value).toBeTrue();
+ expect(navigateSpy).toHaveBeenCalledWith(
+ '/domain/context/entity/101/update/information',
+ {
+ queryParamsHandling: 'merge',
+ queryParams: { search: 'team-test', showWizard: true }
+ }
+ );
+
+ done();
+ });
+ });
+
+ it('should verify will reject Promise when no NavigationAction is provided', (done) => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ {},
+ {},
+ { search: 'team-test' },
+ null,
+ 'delivery'
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ routerServiceStub.getState.and.returnValue(of(routeState));
+ const navigateSpy = spyOn(service, 'navigate').and.returnValue(Promise.resolve(true));
+ const consoleErrorSpy = spyOn(console, 'error').and.callThrough();
+
+ // When
+ const promise = service.navigateTo();
+
+ // Then
+ expect(promise).toBeInstanceOf(Promise);
+ expect(routerServiceStub.getState).toHaveBeenCalled();
+ promise.then((value) => {
+ expect(value).toBeFalse();
+ expect(navigateSpy).not.toHaveBeenCalled();
+ expect(consoleErrorSpy).toHaveBeenCalled();
+
+ done();
+ });
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.ts
new file mode 100644
index 0000000000..e7819df918
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/navigation/services/navigation.service.ts
@@ -0,0 +1,349 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import { ActivatedRoute, ActivatedRouteSnapshot, NavigationExtras, Router } from '@angular/router';
+
+import { take } from 'rxjs/operators';
+
+import { ArrayElement, CollectionsUtil } from '../../../utils';
+
+import { Replacer, TaurusNavigateAction } from '../../../common';
+
+import { SE_NAVIGATE, SystemEventHandler, SystemEventHandlerClass } from '../../system-events';
+
+import { RouterService, RouteState } from '../../router';
+import { RouteStateFactory } from '../../router/factory';
+
+/**
+ * ** Service should be provided from the Root injector in Application.
+ *
+ *
+ */
+@Injectable()
+@SystemEventHandlerClass()
+export class NavigationService {
+ private readonly _routeStateFactory: RouteStateFactory;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(private readonly router: Router,
+ private readonly routerService: RouterService) {
+ this._routeStateFactory = new RouteStateFactory();
+ }
+
+ /**
+ * ** Intercept SE_NAVIGATE Event and handle (react) on it.
+ */
+ @SystemEventHandler(SE_NAVIGATE)
+ _navigationSystemEventHandler_(payload: { url: string | string[]; extras?: NavigationExtras }): Promise {
+ if (CollectionsUtil.isNil(payload)) {
+ return Promise.resolve(false);
+ }
+
+ return this.navigate(payload.url, payload.extras);
+ }
+
+ /**
+ * ** Navigate to url with provided extras.
+ */
+ navigate(url: string | string[], extras?: NavigationExtras): Promise {
+ let urlChunks: string[];
+
+ if (CollectionsUtil.isArray(url)) {
+ urlChunks = url
+ .map((v, i) =>
+ i === 0
+ ? `/${ v.replace(/^\//, '') }`
+ : v
+ );
+ } else if (CollectionsUtil.isString(url)) {
+ if (url.trim() === '/') {
+ urlChunks = ['/'];
+ } else {
+ urlChunks = url
+ .split('/')
+ .filter((v) => !!v)
+ .map((v, i) =>
+ i === 0
+ ? `/${ v }`
+ : v
+ );
+ }
+ } else {
+ return Promise.resolve(false);
+ }
+
+ return CollectionsUtil.isLiteralObject(extras)
+ ? this.router.navigate(urlChunks, extras)
+ : this.router.navigate(urlChunks);
+ }
+
+ /**
+ * ** Navigate to using Route config, RouteState and leveraging provided optional replaceValueResolver.
+ *
+ * @param replaceValues is Object with mapping between Replacer.replaceValue key pointer to dynamic value specific for the Page.
+ * - What does it mean?
+ * Replacer has searchValue and replaceValue. When searchValue match, it replace with replaceValue, but
+ * replaceValue could be something like '$.team', '$.job', '{0}', '{1}' etc... dynamic params, so those params
+ * could be key in this Map to construct correct url depending of the logic in RouteConfig.
+ *
+ * Important!!!
+ * - If replaceValues is not provided or some key doesn't exist there, replaceValue would be use as it is in the RouteConfig.
+ *
+ *
+ * @param activatedRoute is optional and could be provided if you want those state to be used to extract RouteConfig and NavigationAction.
+ */
+ navigateTo(replaceValues?: { [key: string]: ArrayElement['replaceValue'] },
+ activatedRoute?: ActivatedRoute | ActivatedRouteSnapshot): Promise {
+ return this._navigateWithAction(replaceValues, activatedRoute, 'navigateTo');
+ }
+
+ /**
+ * ** Navigate back using Route config, RouteState and leveraging provided optional replaceValueResolver.
+ *
+ * @param replaceValues is Object with mapping between Replacer.replaceValue key pointer to dynamic value specific for the Page.
+ * - What does it mean?
+ * Replacer has searchValue and replaceValue. When searchValue match, it replace with replaceValue, but
+ * replaceValue could be something like '$.team', '$.job', '{0}', '{1}' etc... dynamic params, so those params
+ * could be key in this Map to construct correct url depending of the logic in RouteConfig.
+ *
+ * Important!!!
+ * - If replaceValues is not provided or some key doesn't exist there, replaceValue would be use as it is in the RouteConfig.
+ *
+ *
+ * @param activatedRoute is optional and could be provided if you want those state to be used to extract RouteConfig and NavigationAction.
+ */
+ navigateBack(replaceValues?: { [key: string]: ArrayElement['replaceValue'] },
+ activatedRoute?: ActivatedRoute | ActivatedRouteSnapshot): Promise {
+ return this._navigateWithAction(replaceValues, activatedRoute, 'navigateBack');
+ }
+
+ /**
+ * ** Redirect using Route config, RouteState and leveraging provided optional replaceValueResolver.
+ *
+ * @param replaceValues is Object with mapping between Replacer.replaceValue key pointer to dynamic value specific for the Page.
+ * - What does it mean?
+ * Replacer has searchValue and replaceValue. When searchValue match, it replace with replaceValue, but
+ * replaceValue could be something like '$.team', '$.job', '{0}', '{1}' etc... dynamic params, so those params
+ * could be key in this Map to construct correct url depending of the logic in RouteConfig.
+ *
+ * Important!!!
+ * - If replaceValues is not provided or some key doesn't exist there, replaceValue would be use as it is in the RouteConfig.
+ *
+ *
+ * @param activatedRoute is optional and could be provided if you want those state to be used to extract RouteConfig and NavigationAction.
+ */
+ redirect(replaceValues?: { [key: string]: ArrayElement['replaceValue'] },
+ activatedRoute?: ActivatedRoute | ActivatedRouteSnapshot): Promise {
+ return this._navigateWithAction(replaceValues, activatedRoute, 'redirect')
+ }
+
+ /**
+ * ** Resolve NavigationAction using Route config, RouteState and leveraging provided optional replaceValueResolver.
+ * @param navigateActionType is the type of the NavigationAction we want to resolve from Route config.
+ * @param replaceValues is Object with mapping between Replacer.replaceValue key pointer to dynamic value specific for the Page.
+ * - What does it mean?
+ * Replacer has searchValue and replaceValue. When searchValue match, it replace with replaceValue, but
+ * replaceValue could be something like '$.team', '$.job', '{0}', '{1}' etc... dynamic params, so those params
+ * could be key in this Map to construct correct url depending of the logic in RouteConfig.
+ *
+ * Important!!!
+ * - If replaceValues is not provided or some key doesn't exist there, replaceValue would be use as it is in the RouteConfig.
+ *
+ *
+ * @param activatedRoute is optional and could be provided if you want those state to be used to extract RouteConfig and NavigationAction.
+ */
+ resolveNavigateActionUrl(navigateActionType: 'navigateTo' | 'navigateBack' | 'redirect',
+ replaceValues?: { [key: string]: ArrayElement['replaceValue'] },
+ activatedRoute?: ActivatedRoute | ActivatedRouteSnapshot | RouteState): Promise<[string, NavigationExtras]> {
+
+ const _replaceValues = CollectionsUtil.isLiteralObject(replaceValues)
+ ? replaceValues
+ : {};
+
+ return this
+ ._extractRouteState(activatedRoute)
+ .then((state) => this._constructNavigateParameters(state, navigateActionType))
+ .then((params) =>
+ this._resolveNavigateValues(
+ params.url,
+ params.replacers,
+ _replaceValues,
+ params.navigationExtras
+ )
+ );
+ }
+
+ /**
+ * ** Initialize the Service without any operation, just to create singleton Service instance.
+ *
+ * - It should be done in the root of the project only once.
+ * - Possible place is AppComponent or some root initializer guard.
+ */
+ initialize(): void {
+ this.routerService.initialize();
+ }
+
+ private _navigateWithAction(
+ replaceValues: { [key: string]: ArrayElement['replaceValue'] },
+ activatedRoute: ActivatedRoute | ActivatedRouteSnapshot,
+ dataKey: 'navigateTo' | 'navigateBack' | 'redirect'): Promise {
+
+ return this.resolveNavigateActionUrl(dataKey, replaceValues, activatedRoute)
+ .then()
+ .then(([url, navigationExtras]) => this.navigate(url, navigationExtras))
+ .catch((error) => {
+ console.error(error);
+
+ return false;
+ });
+ }
+
+ private _extractRouteState(activatedRoute: ActivatedRoute | ActivatedRouteSnapshot | RouteState): Promise {
+ // NOTICE
+ // this piece of code is needed for Guards cases usage.
+ // Use params and data from Guard RouteState instead from RouterService because
+ // RouterService state is populated after all guards are resolved
+ if (CollectionsUtil.isDefined(activatedRoute)) {
+ if (activatedRoute instanceof RouteState) {
+ return Promise.resolve(activatedRoute);
+ }
+
+ return Promise.resolve(
+ this._routeStateFactory
+ .create(
+ activatedRoute instanceof ActivatedRoute
+ ? activatedRoute.snapshot
+ : activatedRoute,
+ null
+ )
+ );
+ }
+
+ return this.routerService
+ .getState()
+ .pipe(
+ take(1)
+ )
+ .toPromise()
+ .then((state) => state);
+ }
+
+ // eslint-disable-next-line max-len
+ private _constructNavigateParameters(state: RouteState, dataKey: 'navigateTo' | 'navigateBack' | 'redirect'): Promise {
+ const navigateAction = state.getData(dataKey);
+ const navigationExtras: NavigationExtras = { queryParamsHandling: 'merge' };
+
+ let url: string;
+ let replacers: Array> = [];
+
+ if (CollectionsUtil.isDefined(navigateAction)) {
+ url = this._resolveNavigateUrl(state, dataKey, navigateAction);
+
+ if (CollectionsUtil.isArray(navigateAction.replacers)) {
+ replacers = navigateAction.replacers;
+ }
+
+ this._appendNavigateExtras(state, navigationExtras, navigateAction);
+ } else if (dataKey === 'navigateBack') {
+ url = state.getParentAbsoluteRoutePath();
+ } else {
+ return Promise.reject('Cannot navigate without NavigationAction');
+ }
+
+ return Promise.resolve({
+ url,
+ replacers,
+ navigationExtras
+ });
+ }
+
+ private _resolveNavigateUrl(state: RouteState,
+ dataKey: 'navigateTo' | 'navigateBack' | 'redirect',
+ navigateAction: TaurusNavigateAction): string {
+
+ if (dataKey === 'redirect' || dataKey === 'navigateBack') {
+ if (navigateAction.path === '$.parent' || navigateAction.path === '$.requested') {
+ return navigateAction.useConfigPath
+ ? state.getParentAbsoluteConfigPath()
+ : state.getParentAbsoluteRoutePath();
+ }
+
+ if (navigateAction.path === '$.current') {
+ return navigateAction.useConfigPath
+ ? state.getAbsoluteConfigPath()
+ : state.getAbsoluteRoutePath();
+ }
+
+ return navigateAction.path;
+ }
+
+ if (navigateAction.path === '$.current') {
+ return navigateAction.useConfigPath
+ ? state.getAbsoluteConfigPath()
+ : state.getAbsoluteRoutePath();
+ }
+
+ return navigateAction.path;
+ }
+
+ // eslint-disable-next-line max-len
+ private _appendNavigateExtras(state: RouteState, navigationExtras: NavigationExtras, navigateAction: TaurusNavigateAction): void {
+ if (CollectionsUtil.isString(navigateAction.queryParamsHandling)) {
+ navigationExtras.queryParamsHandling = navigateAction.queryParamsHandling;
+
+ if (navigationExtras.queryParamsHandling === 'merge') {
+ navigationExtras.queryParams = {
+ ...state.queryParams,
+ ...(navigateAction.queryParams ?? {})
+ };
+ } else if (navigationExtras.queryParamsHandling !== 'preserve') {
+ navigationExtras.queryParams = navigateAction.queryParams ?? {};
+ }
+ } else if (CollectionsUtil.isNull(navigateAction.queryParamsHandling)) {
+ delete navigationExtras.queryParamsHandling;
+ } else {
+ navigationExtras.queryParams = {
+ ...state.queryParams,
+ ...(navigateAction.queryParams ?? {})
+ };
+ }
+ }
+
+ private _resolveNavigateValues(
+ path: string,
+ replacers: Array>,
+ replaceValues: { [key: string]: ArrayElement['replaceValue'] },
+ navigationExtras: NavigationExtras): Promise<[string, NavigationExtras]> {
+
+ let resolvedPath: string;
+
+ if (CollectionsUtil.isString(path)) {
+ resolvedPath = path;
+ } else {
+ resolvedPath = '/';
+
+ console.error(`RouteConfig error! NavigationAction config, "path" property is missing.`);
+ }
+
+ for (const replacer of replacers) {
+ const replaceValue = replaceValues[replacer.replaceValue] ?? replacer.replaceValue;
+
+ resolvedPath = resolvedPath.replace(replacer.searchValue, replaceValue);
+ }
+
+ return Promise.resolve([resolvedPath, navigationExtras]);
+ }
+}
+
+interface NavigationParameters {
+ url: string;
+ replacers: Array>;
+ navigationExtras: NavigationExtras;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.spec.ts
new file mode 100644
index 0000000000..32a7c9d502
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.spec.ts
@@ -0,0 +1,83 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { CollectionsUtil } from '../../../utils';
+
+import { BaseAction, BaseActionWithPayload, GenericAction } from './base.actions';
+
+describe('BaseActionWithPayload', () => {
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify will throw Error', () => {
+ // Then
+ expect(() => BaseActionWithPayload.of(null, null))
+ .toThrowError('Method have to be overridden in Subclasses.');
+ });
+ });
+ });
+ });
+});
+
+describe('GenericAction', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new GenericAction(null, null);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // Given
+ const type = '[component] Create State';
+ const payload = { state: [1, 2, 3, 4] };
+
+ // When
+ const instance = GenericAction.of(type, payload);
+
+ // Then
+ expect(instance).toBeInstanceOf(GenericAction);
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+
+ expect(instance.type).toEqual(type);
+ expect(instance.payload).toBe(payload);
+ });
+
+ it('should verify factory method will create instance with Task', () => {
+ // Given
+ const type = '[component] Submit Data';
+ const payload = { state: [10, 20, 30, 40] };
+ const task = 'patch_entity';
+ const dateNowISO = new Date().toISOString();
+ const taskIdentifier = `${ task } __ ${ dateNowISO }`;
+ const dateISOSpy = spyOn(CollectionsUtil, 'dateISO').and.returnValue(dateNowISO);
+ const interpolateStringSpy = spyOn(CollectionsUtil, 'interpolateString').and.returnValue(taskIdentifier);
+
+ // When
+ const instance = GenericAction.of(type, payload, task);
+
+ // Then
+ expect(instance).toBeInstanceOf(GenericAction);
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+
+ expect(instance.type).toEqual(type);
+ expect(instance.payload).toBe(payload);
+ expect(instance.task).toEqual(taskIdentifier);
+ expect(dateISOSpy).toHaveBeenCalled();
+ // @ts-ignore
+ expect(interpolateStringSpy).toHaveBeenCalledWith('%s __ %s', task, dateNowISO);
+ });
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.ts
new file mode 100644
index 0000000000..8ff0c5e217
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/base.actions.ts
@@ -0,0 +1,91 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Action } from '@ngrx/store';
+
+import { createTaskIdentifier } from '../../../common';
+
+/**
+ * ** Base Action for Redux Impl.
+ *
+ *
+ */
+export abstract class BaseAction implements Action {
+ /**
+ * ** Type of Action.
+ */
+ readonly type: string;
+
+ /**
+ * ** Constructor.
+ */
+ protected constructor(type: string) {
+ this.type = type;
+ }
+}
+
+/**
+ * ** Base Action with payload for Redux Impl.
+ *
+ *
+ */
+export abstract class BaseActionWithPayload extends BaseAction {
+
+ /**
+ * ** Action payload data.
+ */
+ readonly payload: T;
+
+ /**
+ * ** Action Task.
+ */
+ readonly task: string;
+
+ /**
+ * ** Constructor.
+ */
+ protected constructor(type: string,
+ payload: T,
+ task?: string) {
+ super(type);
+
+ this.payload = payload;
+ this.task = createTaskIdentifier(task);
+ }
+
+ /**
+ * ** Factory method that have to be overridden in Subclasses.
+ */
+ static of(..._args: unknown[]): BaseActionWithPayload {
+ throw new Error('Method have to be overridden in Subclasses.');
+ }
+}
+
+/**
+ * ** Generic Action with payload for Redux Impl.
+ *
+ * - All parameters type, payload and optional task are provided to Constructor.
+ *
+ *
+ */
+export class GenericAction extends BaseActionWithPayload {
+ /**
+ * ** Constructor.
+ */
+ constructor(type: string,
+ payload: T,
+ task?: string) {
+ super(type, payload, task);
+ }
+
+ /**
+ * ** Factory method for Generic Action with type and payload in Constructor.
+ */
+ static override of(type: string, payload: K, task?: string): GenericAction {
+ return new GenericAction(type, payload, task);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/index.ts
new file mode 100644
index 0000000000..6f669d9c61
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/actions/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './base.actions';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/index.ts
new file mode 100644
index 0000000000..9dcd8f6725
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './ngrx-config.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.spec.ts
new file mode 100644
index 0000000000..fa9dd5d0f5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.spec.ts
@@ -0,0 +1,32 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NGRX_STORE_CONFIG, NGRX_STORE_DEVTOOLS_CONFIG } from './ngrx-config.model';
+
+describe('NGRX_STORE_DEVTOOLS_CONFIG', () => {
+ it('should verify default values are set', () => {
+ // Then
+ expect(NGRX_STORE_DEVTOOLS_CONFIG).toEqual({
+ maxAge: 100,
+ serialize: true,
+ logOnly: false,
+ name: 'Taurus NgRx Store'
+ });
+ });
+});
+
+describe('NGRX_STORE_CONFIG', () => {
+ it('should verify default values are set', () => {
+ // Then
+ expect(NGRX_STORE_CONFIG).toEqual({
+ runtimeChecks: {
+ strictActionImmutability: true,
+ strictStateImmutability: true
+ }
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.ts
new file mode 100644
index 0000000000..635e4a46c1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/config/ngrx-config.model.ts
@@ -0,0 +1,48 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { RootStoreConfig } from '@ngrx/store';
+import { StoreDevtoolsConfig } from '@ngrx/store-devtools';
+
+/**
+ * ** Configuration for NgRx Store Devtools.
+ *
+ *
+ */
+export const NGRX_STORE_DEVTOOLS_CONFIG: StoreDevtoolsConfig = {
+ maxAge: 100,
+ serialize: true,
+ logOnly: false,
+ name: 'Taurus NgRx Store'
+};
+
+
+/**
+ * ** Configuration for NgRx Store.
+ *
+ *
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const NGRX_STORE_CONFIG: RootStoreConfig = {
+ runtimeChecks: {
+ strictActionImmutability: true,
+ strictStateImmutability: true
+ }
+};
+
+/**
+ * ** Config for Taurus impl of NgRx.
+ *
+ *
+ */
+export interface TaurusNgRxConfig {
+ /**
+ * ** StoreDevTools configuration replica.
+ * see {@link https://ngrx.io/guide/store-devtools/config}
+ */
+ storeDevToolsConfig?: StoreDevtoolsConfig;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/index.ts
new file mode 100644
index 0000000000..2fcf8e79a2
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './root-effects.registry';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.spec.ts
new file mode 100644
index 0000000000..97b0369487
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.spec.ts
@@ -0,0 +1,17 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { RouterEffects } from '../../router/state/effects';
+
+import { SHARED_ROOT_EFFECTS } from './root-effects.registry';
+
+describe('SHARED_ROOT_EFFECTS', () => {
+ it('should verify expected Effects are in this registry', () => {
+ // Then
+ expect(SHARED_ROOT_EFFECTS).toEqual([RouterEffects]);
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.ts
new file mode 100644
index 0000000000..3c4e25d4db
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/effects/root-effects.registry.ts
@@ -0,0 +1,20 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Type } from '@angular/core';
+
+import { RouterEffects } from '../../router/state/effects';
+
+/**
+ * ** Registry for Root Effects.
+ *
+ *
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const SHARED_ROOT_EFFECTS: Array> = [
+ RouterEffects
+];
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/index.ts
new file mode 100644
index 0000000000..8f1199e1f4
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './taurus-shared-ngrx-dev.module';
+export * from './taurus-shared-ngrx-prod.module';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-dev.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-dev.module.ts
new file mode 100644
index 0000000000..f19b4f4670
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-dev.module.ts
@@ -0,0 +1,65 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ModuleWithProviders, NgModule } from '@angular/core';
+
+import { StoreModule } from '@ngrx/store';
+import { StoreRouterConnectingModule } from '@ngrx/router-store';
+import { EffectsModule } from '@ngrx/effects';
+import { StoreDevtoolsConfig, StoreDevtoolsModule } from '@ngrx/store-devtools';
+
+import { RouterService, RouterServiceImpl } from '../../router';
+import { SharedRouterSerializer } from '../../router/integration/ngrx';
+
+import { ComponentService, ComponentServiceImpl } from '../../component';
+
+import { SHARED_ROOT_REDUCERS } from '../reducers';
+import { SHARED_ROOT_EFFECTS } from '../effects';
+import { NGRX_STORE_CONFIG, NGRX_STORE_DEVTOOLS_CONFIG } from '../config';
+import { STORE_ROUTER } from '../state';
+
+/**
+ * ** Taurus NgRx Redux module recommended for use in Development builds.
+ *
+ *
+ */
+@NgModule({
+ imports: [
+ StoreModule.forRoot(SHARED_ROOT_REDUCERS, NGRX_STORE_CONFIG),
+ EffectsModule.forRoot(SHARED_ROOT_EFFECTS),
+ StoreDevtoolsModule.instrument(() => TaurusSharedNgrxDevModule.storeDevToolsConfig),
+ StoreRouterConnectingModule.forRoot({
+ stateKey: STORE_ROUTER,
+ serializer: SharedRouterSerializer
+ })
+ ],
+ exports: [
+ StoreModule,
+ EffectsModule,
+ StoreDevtoolsModule,
+ StoreRouterConnectingModule
+ ]
+})
+export class TaurusSharedNgrxDevModule {
+ private static storeDevToolsConfig: StoreDevtoolsConfig = NGRX_STORE_DEVTOOLS_CONFIG;
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-explicit-any
+ static forRoot(config: StoreDevtoolsConfig = {} as any): ModuleWithProviders {
+ TaurusSharedNgrxDevModule.storeDevToolsConfig = {
+ ...NGRX_STORE_DEVTOOLS_CONFIG,
+ ...config
+ };
+
+ return {
+ ngModule: TaurusSharedNgrxDevModule,
+ providers: [
+ { provide: RouterService, useClass: RouterServiceImpl },
+ { provide: ComponentService, useClass: ComponentServiceImpl }
+ ]
+ };
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-prod.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-prod.module.ts
new file mode 100644
index 0000000000..5bf0570272
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/helper-modules/taurus-shared-ngrx-prod.module.ts
@@ -0,0 +1,54 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ModuleWithProviders, NgModule } from '@angular/core';
+
+import { StoreModule } from '@ngrx/store';
+import { StoreRouterConnectingModule } from '@ngrx/router-store';
+import { EffectsModule } from '@ngrx/effects';
+
+import { RouterService, RouterServiceImpl } from '../../router';
+import { SharedRouterSerializer } from '../../router/integration/ngrx';
+
+import { ComponentService, ComponentServiceImpl } from '../../component';
+
+import { SHARED_ROOT_REDUCERS } from '../reducers';
+import { SHARED_ROOT_EFFECTS } from '../effects';
+import { NGRX_STORE_CONFIG } from '../config';
+import { STORE_ROUTER } from '../state';
+
+/**
+ * ** Taurus NgRx Redux module recommended for use in Production builds.
+ *
+ *
+ */
+@NgModule({
+ imports: [
+ StoreModule.forRoot(SHARED_ROOT_REDUCERS, NGRX_STORE_CONFIG),
+ EffectsModule.forRoot(SHARED_ROOT_EFFECTS),
+ StoreRouterConnectingModule.forRoot({
+ stateKey: STORE_ROUTER,
+ serializer: SharedRouterSerializer
+ })
+ ],
+ exports: [
+ StoreModule,
+ EffectsModule,
+ StoreRouterConnectingModule
+ ]
+})
+export class TaurusSharedNgrxProdModule {
+ static forRoot(): ModuleWithProviders {
+ return {
+ ngModule: TaurusSharedNgrxProdModule,
+ providers: [
+ { provide: RouterService, useClass: RouterServiceImpl },
+ { provide: ComponentService, useClass: ComponentServiceImpl }
+ ]
+ };
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/index.ts
new file mode 100644
index 0000000000..8acf801157
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/index.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './actions';
+export * from './state';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/public-api.ts
new file mode 100644
index 0000000000..ba142389bd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/public-api.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/index.ts
new file mode 100644
index 0000000000..681b00b6bc
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './shared-root-reducers.registry';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.spec.ts
new file mode 100644
index 0000000000..2cdc95b5b9
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.spec.ts
@@ -0,0 +1,24 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { routerReducer } from '../../router/state/reducers';
+
+import { componentReducer } from '../../component';
+
+import { STORE_COMPONENTS, STORE_ROUTER } from '../state';
+
+import { SHARED_ROOT_REDUCERS } from './shared-root-reducers.registry';
+
+describe('SHARED_ROOT_REDUCERS', () => {
+ it('should verify root reducers are registered', () => {
+ // Then
+ expect(SHARED_ROOT_REDUCERS).toEqual({
+ [STORE_ROUTER]: routerReducer,
+ [STORE_COMPONENTS]: componentReducer
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.ts
new file mode 100644
index 0000000000..4e0291d602
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/reducers/shared-root-reducers.registry.ts
@@ -0,0 +1,23 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ActionReducerMap } from '@ngrx/store';
+
+import { STORE_COMPONENTS, STORE_ROUTER, StoreState } from '../state';
+
+import { routerReducer } from '../../router/state/reducers';
+import { componentReducer } from '../../component/state';
+
+/**
+ * ** Root reducers for Shared.
+ *
+ *
+ */
+export const SHARED_ROOT_REDUCERS: ActionReducerMap = {
+ [STORE_ROUTER]: routerReducer,
+ [STORE_COMPONENTS]: componentReducer
+};
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/index.ts
new file mode 100644
index 0000000000..d83955450f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './store-state.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/store-state.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/store-state.model.ts
new file mode 100644
index 0000000000..1356c3af04
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/state/store-state.model.ts
@@ -0,0 +1,29 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { RouterState } from '../../router';
+import { LiteralComponentsState } from '../../component';
+
+/**
+ * ** Constant for Store Router field.
+ */
+export const STORE_ROUTER = 'router';
+
+/**
+ * ** Constant for Store Components field.
+ */
+export const STORE_COMPONENTS = 'components';
+
+/**
+ * ** Store State interface.
+ *
+ *
+ */
+export interface StoreState {
+ [STORE_ROUTER]: RouterState;
+ [STORE_COMPONENTS]: LiteralComponentsState;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/taurus-shared-ngrx.module.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/taurus-shared-ngrx.module.ts
new file mode 100644
index 0000000000..2362ffd93b
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/ngrx/taurus-shared-ngrx.module.ts
@@ -0,0 +1,81 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/unified-signatures */
+
+import { InjectionToken, ModuleWithProviders, NgModule, Type } from '@angular/core';
+
+import { Action, ActionReducer, ActionReducerMap, StoreConfig, StoreFeatureModule, StoreModule } from '@ngrx/store';
+import { EffectsFeatureModule, EffectsModule } from '@ngrx/effects';
+
+import { TaurusNgRxConfig } from './config';
+import { TaurusSharedNgrxProdModule, TaurusSharedNgrxDevModule } from './helper-modules';
+
+/**
+ * ** Integration Class module for NgRx Redux.
+ *
+ *
+ */
+@NgModule({})
+export class TaurusSharedNgRxModule {
+ /**
+ * ** Provides TaurusSharedNgrxProdModule and all Services related to Taurus Redux.
+ *
+ * - Recommended for Prod (release) builds.
+ * - Should be invoke at Root and only once for entire project.
+ * - In FeaturesModules (lazy loaded Modules) invoke one of the methods forFeatureEffects/forFeatureStore .
+ */
+ static forRoot(): ModuleWithProviders {
+ return TaurusSharedNgrxProdModule.forRoot();
+ }
+
+ /**
+ * ** Provides TaurusSharedNgrxDevModule including StoreDevTools and all Services related to Taurus Redux.
+ *
+ * - Recommended for Dev (local) builds.
+ * - Should be invoke at Root and only once for entire project.
+ * - In FeaturesModules (lazy loaded Modules) invoke one of the methods forFeatureEffects/forFeatureStore .
+ */
+ static forRootWithDevtools(config: TaurusNgRxConfig = {}): ModuleWithProviders {
+ return TaurusSharedNgrxDevModule.forRoot(config.storeDevToolsConfig);
+ }
+
+ /**
+ * ** Load Features Effects.
+ *
+ * - Should be invoke in FeatureModules (lazy loaded Modules).
+ * - It will register Effects for that Feature.
+ */
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ static forFeatureEffects(effects: Array>): ModuleWithProviders {
+ return EffectsModule.forFeature(effects);
+ }
+
+ /**
+ * ** Load Features Store reducers.
+ *
+ * - Should be invoke in FeatureModules (lazy loaded Modules).
+ * - It will extend Store and add reducers for that Feature.
+ */
+ static forFeatureStore(
+ featureName: string,
+ reducers: ActionReducerMap | InjectionToken>,
+ config?: StoreConfig | InjectionToken>): ModuleWithProviders;
+ static forFeatureStore(
+ featureName: string,
+ reducer: ActionReducer | InjectionToken>,
+ config?: StoreConfig | InjectionToken>): ModuleWithProviders;
+ static forFeatureStore(
+ featureName: string,
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ reducer: any,
+ config?: StoreConfig | InjectionToken>): ModuleWithProviders {
+
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
+ return StoreModule.forFeature(featureName, reducer, config);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/public-api.ts
new file mode 100644
index 0000000000..d97123f0dd
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/public-api.ts
@@ -0,0 +1,13 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './component/public-api';
+export * from './navigation/public-api';
+export * from './ngrx/public-api';
+export * from './router/public-api';
+export * from './system-events/public-api';
+export * from './url-state-manager/public-api';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/index.ts
new file mode 100644
index 0000000000..c03c42df7f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './route-state.factory';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.spec.ts
new file mode 100644
index 0000000000..523ac14a70
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.spec.ts
@@ -0,0 +1,54 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { createRouteSnapshot } from '../../../unit-testing';
+
+import { RouteSegments, RouteState } from '../model';
+
+import { RouteStateFactory } from './route-state.factory';
+
+describe('RouteStateFactory', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RouteStateFactory();
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Methods::', () => {
+ describe('|create|', () => {
+ it('should verify will return RouteState from provided ActivatedRouteSnapshot', () => {
+ // Given
+ const routeSnapshot = createRouteSnapshot({});
+ const factory = new RouteStateFactory();
+ const assertion = RouteState.of(
+ new RouteSegments(
+ 'entity/23',
+ { paramKey: 'prime' },
+ { entityId: 23 },
+ { search: 'test-team' },
+ new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ {},
+ undefined,
+ 'domain/context'),
+ 'entity/23'),
+ 'domain/context/entity/23'
+ );
+
+ // When
+ const routeState = factory.create(routeSnapshot, 'domain/context/entity/23');
+
+ // Then
+ expect(routeState).toEqual(assertion);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.ts
new file mode 100644
index 0000000000..233ab1866d
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/factory/route-state.factory.ts
@@ -0,0 +1,54 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { ActivatedRouteSnapshot, UrlSegment } from '@angular/router';
+
+import { CollectionsUtil } from '../../../utils';
+
+import { RouteSegments, RouteState } from '../model';
+
+/**
+ * ** Route State Factory.
+ *
+ *
+ */
+export class RouteStateFactory {
+ /**
+ * ** Creates Router State from provided Route snapshot.
+ */
+ create(routeSnapshot: ActivatedRouteSnapshot, url: string): RouteState {
+ return RouteState.of(
+ this._getRouteSegments(routeSnapshot),
+ url
+ );
+ }
+
+ private _getRouteSegments(routeSnapshot: ActivatedRouteSnapshot): RouteSegments {
+ if (CollectionsUtil.isNil(routeSnapshot)) {
+ return null;
+ }
+
+ const routePathSegments: string[] = [];
+ routeSnapshot.url.forEach((segment: UrlSegment) => {
+ routePathSegments.push(segment.path);
+ });
+
+ const routePath = routePathSegments.join('/');
+ const data = CollectionsUtil.cloneDeep(routeSnapshot.data);
+ const params = CollectionsUtil.cloneDeep(routeSnapshot.params);
+ const queryParams = CollectionsUtil.cloneDeep(routeSnapshot.queryParams);
+ const configPath = routeSnapshot.routeConfig?.path;
+
+ let parentNavSegments: RouteSegments;
+
+ if (routeSnapshot.parent) {
+ parentNavSegments = this._getRouteSegments(routeSnapshot.parent);
+ }
+
+ return RouteSegments.of(routePath, data, params, queryParams, parentNavSegments, configPath);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/index.ts
new file mode 100644
index 0000000000..6ad912db02
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/index.ts
@@ -0,0 +1,10 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './model';
+export * from './state/actions';
+export * from './services';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/index.ts
new file mode 100644
index 0000000000..497a8d8bcf
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './router-serializer.service';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.spec.ts
new file mode 100644
index 0000000000..00d6ff1b24
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.spec.ts
@@ -0,0 +1,107 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { UrlSegment } from '@angular/router';
+import { TestBed } from '@angular/core/testing';
+
+import { ComponentStub, createRouteSnapshot, RouteConfigStub } from '../../../../unit-testing';
+
+import { RouteSegments, RouteState } from '../../model';
+import { RouteStateFactory } from '../../factory';
+
+import { SharedRouterSerializer } from './router-serializer.service';
+
+describe('SharedRouterSerializer', () => {
+ let service: SharedRouterSerializer;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ SharedRouterSerializer
+ ]
+ });
+
+ service = TestBed.inject(SharedRouterSerializer);
+ });
+
+ it('should verify instance is created', () => {
+ // Then
+ expect(service).toBeDefined();
+ });
+
+ describe('Methods::', () => {
+ describe('|serialize|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const routeState = RouteState.of(
+ RouteSegments.of(
+ 'delivery',
+ { paramKey: 'prime' },
+ { entityId: 25 },
+ { search: 'test-team' },
+ RouteSegments.of(
+ 'entity/25',
+ { deferredLoading: true },
+ { entityId: 25 },
+ { search: 'test-team' },
+ RouteSegments.of(
+ 'domain/context',
+ { entityKey: 'second' },
+ {},
+ { search: 'test-team' },
+ null
+ )
+ )
+ ),
+ 'domain/context/entity/25/delivery'
+ );
+ const factoryCreateSpy = spyOn(RouteStateFactory.prototype, 'create').and.returnValue(routeState);
+
+ const routeSnapshotChild2 = createRouteSnapshot({
+ url: [new UrlSegment('domain/context', {}), new UrlSegment('entity/25', {}), new UrlSegment('delivery', {})],
+ data: { entityKey: 'second' },
+ params: { entityId: 25 },
+ queryParams: { search: 'team-test' },
+ outlet: 'router-outlet',
+ component: ComponentStub,
+ routeConfig: new RouteConfigStub('delivery', ComponentStub, 'router-outlet')
+ });
+ const routeSnapshotChild1 = createRouteSnapshot({
+ url: [new UrlSegment('domain/context', {}), new UrlSegment('entity/25', {})],
+ data: { deferredLoading: true },
+ params: { entityId: 25 },
+ queryParams: { search: 'team-test' },
+ outlet: 'router-outlet',
+ component: ComponentStub,
+ routeConfig: new RouteConfigStub('entity/25', ComponentStub, 'router-outlet'),
+ firstChild: routeSnapshotChild2
+ });
+ const rootRouteSnapshot = createRouteSnapshot({
+ url: [new UrlSegment('domain/context', {})],
+ data: { paramKey: 'prime' },
+ params: {},
+ queryParams: { search: 'team-test' },
+ outlet: 'router-outlet',
+ component: ComponentStub,
+ routeConfig: new RouteConfigStub('domain/context', ComponentStub, 'router-outlet'),
+ firstChild: routeSnapshotChild1
+ });
+
+ // When
+ const result = service.serialize({
+ url: 'domain/context/entity/25/delivery',
+ root: rootRouteSnapshot,
+ toString: () => ''
+ });
+
+ // Then
+ expect(factoryCreateSpy).toHaveBeenCalledWith(routeSnapshotChild2, 'domain/context/entity/25/delivery');
+ expect(result).toBe(routeState);
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.ts
new file mode 100644
index 0000000000..5006876b44
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/integration/ngrx/router-serializer.service.ts
@@ -0,0 +1,44 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Injectable } from '@angular/core';
+import { RouterStateSnapshot } from '@angular/router';
+
+import { RouterStateSerializer } from '@ngrx/router-store';
+
+import { RouteState } from '../../model';
+import { RouteStateFactory } from '../../factory';
+
+/**
+ * ** Shared Router Serializer implements NgRx RouterStateSerializer.
+ *
+ *
+ */
+@Injectable()
+export class SharedRouterSerializer implements RouterStateSerializer {
+ private readonly _routeStateFactory: RouteStateFactory;
+
+ /**
+ * ** Constructor.
+ */
+ constructor() {
+ this._routeStateFactory = new RouteStateFactory();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ serialize(routerState: RouterStateSnapshot): RouteState {
+ let routeSnapshot = routerState.root;
+
+ while (routeSnapshot.firstChild) {
+ routeSnapshot = routeSnapshot.firstChild;
+ }
+
+ return this._routeStateFactory.create(routeSnapshot, routerState.url);
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/index.ts
new file mode 100644
index 0000000000..db0bb6637c
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './route.model';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.spec.ts
new file mode 100644
index 0000000000..ac74027172
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.spec.ts
@@ -0,0 +1,792 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { RouterState, RouteSegments, RouteState } from './route.model';
+
+describe('Route Classes', () => {
+ describe('RouteSegments', () => {
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ {},
+ null,
+ null);
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RouteSegments.of('domain/context',
+ {},
+ {},
+ null,
+ null);
+
+ // Then
+ expect(instance).toBeInstanceOf(RouteSegments);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with default values', () => {
+ // When
+ const instance = RouteSegments.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RouteSegments);
+ expect(instance.params).toEqual({});
+ expect(instance.queryParams).toEqual({});
+ expect(instance.routePath).toEqual('');
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|getData|', () => {
+ it('should verify will return param', () => {
+ // Given
+ const routeSegments = RouteSegments.of(
+ 'bill/105/view',
+ { paramKey: 'prime' },
+ { billId: '105' },
+ {},
+ RouteSegments.of(
+ 'entity/17/explore',
+ { deferredLoad: true },
+ { entityId: '17' },
+ null,
+ RouteSegments.of(
+ 'domain/context',
+ null,
+ null,
+ null,
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId/explore'
+ ),
+ 'bill/:billId/view'
+ );
+
+ // When
+ const param1 = routeSegments.getData('randomParam');
+ const param2 = routeSegments.getData('paramKey');
+ const param3 = routeSegments.getData('deferredLoad');
+
+ // Then
+ expect(param1).toBeUndefined();
+ expect(param2).toEqual('prime');
+ expect(param3).toEqual(true);
+ });
+ });
+
+ describe('|getParam|', () => {
+ it('should verify will return param', () => {
+ // Given
+ const routeSegments = RouteSegments.of(
+ 'bill/105',
+ {},
+ { billId: '105' },
+ {},
+ RouteSegments.of(
+ 'entity/17',
+ {},
+ { entityId: '17' },
+ {},
+ null,
+ 'entity/:entityId'
+ ),
+ 'bill/:billId'
+ );
+
+ // When
+ const param1 = routeSegments.getParam('randomParam');
+ const param2 = routeSegments.getParam('billId');
+ const param3 = routeSegments.getParam('entityId');
+
+ // Then
+ expect(param1).toBeUndefined();
+ expect(param2).toEqual('105');
+ expect(param3).toEqual('17');
+ });
+ });
+
+ describe('|getQueryParam|', () => {
+ it('should verify will return queryParam', () => {
+ // Given
+ const routeSegments = RouteSegments.of(
+ 'bill/105',
+ {},
+ { billId: '105' },
+ { search: 'test-team', activeUser: 'aUser' },
+ RouteSegments.of(
+ 'entity/17',
+ {},
+ { entityId: '17' },
+ { search: 'test-team', activeUser: 'aUser' },
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ {},
+ { search: 'test-team', activeUser: 'aUser' },
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId'
+ ),
+ 'bill/:billId'
+ );
+
+ // When
+ const queryParam1 = routeSegments.getQueryParam('entityId');
+ const queryParam2 = routeSegments.getQueryParam('search');
+ const queryParam3 = routeSegments.getQueryParam('activeUser');
+
+ // Then
+ expect(queryParam1).toBeUndefined();
+ expect(queryParam2).toEqual('test-team');
+ expect(queryParam3).toEqual('aUser');
+ });
+ });
+ });
+
+ describe('Getters/Setters::', () => {
+ describe('|GET -> routePathSegments|', () => {
+ it('should verify will return routePathSegments', () => {
+ // Given
+ const routeSegments = RouteSegments.of(
+ 'bill/105/view',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'entity/17/explore',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'domain/context',
+ null,
+ null,
+ null,
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId/explore'
+ ),
+ 'bill/:billId/view'
+ );
+
+ // When
+ const routePathSegments = routeSegments.routePathSegments;
+
+ // Then
+ expect(routePathSegments).toEqual(['domain/context', 'entity/17/explore', 'bill/105/view']);
+ });
+ });
+
+ describe('|GET -> configPathSegments|', () => {
+ it('should verify will return configPathSegments', () => {
+ // Given
+ const routeSegments = RouteSegments.of(
+ 'bill/105/view',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'entity/17/explore',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'domain/context',
+ null,
+ null,
+ null,
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId/explore'
+ ),
+ 'bill/:billId/view'
+ );
+
+ // When
+ const configPathSegments = routeSegments.configPathSegments;
+
+ // Then
+ expect(configPathSegments).toEqual(['domain/context', 'entity/:entityId/explore', 'bill/:billId/view']);
+ });
+ });
+ });
+ });
+
+ describe('RouteState', () => {
+ let routeState: RouteState;
+ let routeSegments: RouteSegments;
+
+ beforeEach(() => {
+ routeSegments = RouteSegments.of(
+ 'bill/105/view',
+ {},
+ null,
+ { 'test_search*': '^test-team$', 'test_@active-user': '%aUser%' },
+ RouteSegments.of(
+ 'entity/17/explore',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ null,
+ null,
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId/explore'
+ ),
+ 'bill/:billId/view'
+ );
+
+ routeState = RouteState.of(
+ routeSegments,
+ 'domain/context/entity/17/explore/bill/105/view'
+ );
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RouteState(
+ new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ null,
+ null),
+ 'domain/context'
+ );
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RouteState.of(
+ new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ null,
+ null),
+ 'domain/context'
+ );
+
+ // Then
+ expect(instance).toBeInstanceOf(RouteState);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with default values', () => {
+ // When
+ const instance = RouteState.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RouteState);
+ expect(instance.routeSegments).toEqual(RouteSegments.empty());
+ expect(instance.url).toEqual('');
+ });
+ });
+ });
+ });
+
+ describe('Getters/Setters::', () => {
+ describe('|GET -> absoluteRoutePath|', () => {
+ it('should verify will return location', () => {
+ // When
+ const location = routeState.absoluteRoutePath;
+
+ // Then
+ expect(location).toEqual('/domain/context/entity/17/explore/bill/105/view');
+ });
+ });
+
+ describe('|GET -> routePath|', () => {
+ it('should verify will return routePath', () => {
+ // When
+ const routePath = routeState.routePath;
+
+ // Then
+ expect(routePath).toEqual('bill/105/view');
+ });
+ });
+
+ describe('|GET -> routePathSegments|', () => {
+ it('should verify will return routePathSegments', () => {
+ // When
+ const routePathSegments = routeState.routePathSegments;
+
+ // Then
+ expect(routePathSegments).toEqual(['domain/context', 'entity/17/explore', 'bill/105/view']);
+ });
+ });
+
+ describe('|GET -> configPath|', () => {
+ it('should verify will return configPath', () => {
+ // When
+ const configPath = routeState.configPath;
+
+ // Then
+ expect(configPath).toEqual('bill/:billId/view');
+ });
+ });
+
+ describe('|GET -> absoluteConfigPath|', () => {
+ it('should verify will return absoluteConfigPath', () => {
+ // When
+ const absoluteConfigPath = routeState.absoluteConfigPath;
+
+ // Then
+ expect(absoluteConfigPath).toEqual('/domain/context/entity/:entityId/explore/bill/:billId/view');
+ });
+ });
+
+ describe('|GET -> configPathSegments|', () => {
+ it('should verify will return configPathSegments', () => {
+ // When
+ const configPathSegments = routeState.configPathSegments;
+
+ // Then
+ expect(configPathSegments).toEqual(['domain/context', 'entity/:entityId/explore', 'bill/:billId/view']);
+ });
+ });
+
+ describe('|GET -> queryParams|', () => {
+ it('should verify will return queryParams', () => {
+ // When
+ const queryParams = routeState.queryParams;
+
+ // Then
+ expect(queryParams).toBe(routeState.routeSegments.queryParams);
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|serializeQueryParams|', () => {
+ it('should verify will serialize queryParams in string', () => {
+ // When
+ const query = routeState.serializeQueryParams();
+
+ // Then
+ expect(query).toEqual('test_search*=%5Etest-team%24&test_%40active-user=%25aUser%25');
+ });
+
+ it('should verify will return empty string when no queryParams', () => {
+ // Given
+ routeState = RouteState.of(
+ RouteSegments.of(
+ routeState.routeSegments.routePath,
+ routeState.routeSegments.data,
+ routeState.routeSegments.params,
+ {},
+ routeState.routeSegments.parent,
+ routeState.routeSegments.configPath
+ ),
+ routeState.url
+ );
+
+ // When
+ const query = routeState.serializeQueryParams();
+
+ // Then
+ expect(query).toEqual('');
+ });
+ });
+
+ describe('|getUrl|', () => {
+ it('should verify will return correct value', () => {
+ // When
+ const url = routeState.getUrl();
+
+ // Then
+ expect(url).toEqual(
+ '/domain/context/entity/17/explore/bill/105/view?test_search*=%5Etest-team%24&test_%40active-user=%25aUser%25'
+ );
+ });
+ });
+
+ describe('|getData|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const getDataSpy = spyOn(RouteSegments.prototype, 'getData').and.returnValue('loading');
+
+ // When
+ const data = routeState.getData('primeKey');
+
+ // Then
+ expect(getDataSpy).toHaveBeenCalledWith('primeKey');
+ expect(data).toEqual('loading');
+ });
+ });
+
+ describe('|getParam|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const getParamSpy = spyOn(RouteSegments.prototype, 'getParam').and.returnValue('test-value');
+
+ // When
+ const param = routeState.getParam('random-key');
+
+ // Then
+ expect(getParamSpy).toHaveBeenCalledWith('random-key');
+ expect(param).toEqual('test-value');
+ });
+ });
+
+ describe('|getQueryParam|', () => {
+ it('should verify will invoke correct methods', () => {
+ // Given
+ const getQueryParamSpy = spyOn(RouteSegments.prototype, 'getQueryParam').and.returnValue('test-value');
+
+ // When
+ const queryParam = routeState.getQueryParam('random-key');
+
+ // Then
+ expect(getQueryParamSpy).toHaveBeenCalledWith('random-key');
+ expect(queryParam).toEqual('test-value');
+ });
+ });
+
+ describe('|getParentAbsoluteRoutePath|', () => {
+ it('should verify will return correct value', () => {
+ // When
+ const parentAbsoluteRoutePath = routeState.getParentAbsoluteRoutePath();
+
+ // Then
+ expect(parentAbsoluteRoutePath).toEqual('/domain/context/entity/17/explore');
+ });
+ });
+
+ describe('|toJSON|', () => {
+ it('should verify will return correct object for serialization', () => {
+ // When
+ const objForSerialize = routeState.toJSON();
+
+ // Then
+ expect(objForSerialize).toEqual({
+ routeSegments,
+ url: 'domain/context/entity/17/explore/bill/105/view',
+ routePath: 'bill/105/view',
+ absoluteRoutePath: '/domain/context/entity/17/explore/bill/105/view',
+ routePathSegments: ['domain/context', 'entity/17/explore', 'bill/105/view'],
+ configPath: 'bill/:billId/view',
+ absoluteConfigPath: '/domain/context/entity/:entityId/explore/bill/:billId/view',
+ configPathSegments: ['domain/context', 'entity/:entityId/explore', 'bill/:billId/view'],
+ queryParams: { 'test_search*': '^test-team$', 'test_@active-user': '%aUser%' }
+ });
+ });
+ });
+ });
+ });
+
+ describe('RouterState', () => {
+ let routeSegments: RouteSegments;
+ let routeState: RouteState;
+ let routerState: RouterState;
+ let previousRouterState: RouterState;
+
+ beforeEach(() => {
+ routeSegments = RouteSegments.of(
+ 'bill/105/view',
+ {},
+ null,
+ { 'test_search*': '^test-team$', 'test_@active-user': '%aUser%' },
+ RouteSegments.of(
+ 'entity/17/explore',
+ {},
+ null,
+ null,
+ RouteSegments.of(
+ 'domain/context',
+ {},
+ null,
+ null,
+ null,
+ 'domain/context'
+ ),
+ 'entity/:entityId/explore'
+ ),
+ 'bill/:billId/view'
+ );
+
+ routeState = RouteState.of(
+ routeSegments,
+ 'domain/context/entity/17/explore/bill/105/view'
+ );
+
+ routerState = RouterState.of(
+ routeState,
+ 15
+ );
+
+ previousRouterState = RouterState.of(
+ RouteState.empty(),
+ 14
+ );
+ previousRouterState.previousStates.push(
+ RouterState.of(
+ RouteState.empty(),
+ 13
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 12
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 11
+ )
+ );
+ });
+
+ it('should verify instance is created', () => {
+ // When
+ const instance = new RouterState(
+ new RouteState(
+ new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ null,
+ null),
+ 'domain/context'
+ ),
+ 8
+ );
+
+ // Then
+ expect(instance).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RouterState.of(
+ new RouteState(
+ new RouteSegments(
+ 'domain/context',
+ {},
+ {},
+ null,
+ null),
+ 'domain/context'
+ ),
+ 11
+ );
+
+ // Then
+ expect(instance).toBeInstanceOf(RouterState);
+ });
+ });
+
+ describe('|empty|', () => {
+ it('should verify will create empty instance with default values', () => {
+ // When
+ const instance = RouterState.empty();
+
+ // Then
+ expect(instance).toBeInstanceOf(RouterState);
+ expect(instance.state).toEqual(RouteState.empty());
+ expect(instance.navigationId).toEqual(null);
+ expect(instance.previousStates).toEqual([]);
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|getPrevious|', () => {
+ beforeEach(() => {
+ routerState.previousStates.push(
+ ...previousRouterState.previousStates
+ );
+ previousRouterState.previousStates.length = 0;
+ routerState.previousStates.unshift(previousRouterState);
+ });
+
+ it('should verify will return the first before current as default', () => {
+ // When
+ const previous = routerState.getPrevious();
+
+ // Then
+ expect(previous).toBe(previousRouterState);
+ });
+
+ it('should verify will return the first before current for index 0', () => {
+ // When
+ const previous = routerState.getPrevious(0);
+
+ // Then
+ expect(previous).toBe(previousRouterState);
+ });
+
+ it('should verify will return the third before current for index 2', () => {
+ // When
+ const previous = routerState.getPrevious(2);
+
+ // Then
+ expect(previous).toBe(routerState.previousStates[2]);
+ });
+
+ it('should verify will return null for number less than 0', () => {
+ // When
+ const previous = routerState.getPrevious(-1);
+
+ // Then
+ expect(previous).toBe(null);
+ });
+
+ it('should verify will return the first before current for index not a number', () => {
+ // When
+ const previous = routerState.getPrevious(null);
+
+ // Then
+ expect(previous).toBe(previousRouterState);
+ });
+
+ it('should verify will return null for index that is out of bound of stored States', () => {
+ // When
+ const previous = routerState.getPrevious(4);
+
+ // Then
+ expect(previous).toBe(null);
+ });
+ });
+
+ describe('|appendPrevious|', () => {
+ it('should verify will add previousStates to current one', () => {
+ // Given
+ const firstElement = RouterState.of(RouteState.empty(), 14);
+ const forthElement = RouterState.of(RouteState.empty(), 11);
+
+ // When
+ routerState.appendPrevious(previousRouterState);
+
+ // Then
+ expect(routerState.previousStates.length).toEqual(4);
+ expect(routerState.previousStates[0]).toEqual(firstElement);
+ expect(routerState.previousStates[3]).toEqual(forthElement);
+ });
+
+ it('should verify will pop the oldest one and will unshift the new one when buffer has 10 elements', () => {
+ // Given
+ previousRouterState.previousStates.push(
+ RouterState.of(
+ RouteState.empty(),
+ 10
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 9
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 8
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 7
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 6
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 5
+ ),
+ RouterState.of(
+ RouteState.empty(),
+ 4
+ )
+ );
+
+ // Then 1
+ expect(routerState.previousStates.length).toEqual(0);
+ expect(routerState.previousStates[0]).toBeUndefined();
+ expect(previousRouterState.navigationId).toEqual(14);
+ expect(previousRouterState.previousStates[0]).toEqual(
+ RouterState.of(RouteState.empty(), 13)
+ );
+ expect(previousRouterState.previousStates[9]).toEqual(
+ RouterState.of(RouteState.empty(), 4)
+ );
+
+ // When
+ routerState.appendPrevious(previousRouterState);
+
+ // Then 2
+ expect(routerState.previousStates.length).toEqual(10);
+ expect(routerState.previousStates[0]).toEqual(
+ RouterState.of(RouteState.empty(), 14)
+ );
+ expect(routerState.previousStates[9]).toEqual(
+ RouterState.of(RouteState.empty(), 5)
+ );
+ });
+
+ it(`should verify won't add new state when previousState has same navigationId like current`, () => {
+ // Given
+ routerState = RouterState.of(
+ RouteState.empty(),
+ 14
+ );
+
+ // Then 1
+ expect(routerState.previousStates.length).toEqual(0);
+ expect(previousRouterState.previousStates.length).toEqual(3);
+ expect(previousRouterState.previousStates[0]).toEqual(
+ RouterState.of(RouteState.empty(), 13)
+ );
+ expect(previousRouterState.previousStates[2]).toEqual(
+ RouterState.of(RouteState.empty(), 11)
+ );
+
+ // When
+ routerState.appendPrevious(previousRouterState);
+
+ // Then 2
+ expect(routerState.previousStates.length).toEqual(3);
+ expect(routerState.navigationId).toEqual(14);
+ expect(routerState.previousStates[0]).toEqual(
+ RouterState.of(RouteState.empty(), 13)
+ );
+ expect(routerState.previousStates[2]).toEqual(
+ RouterState.of(RouteState.empty(), 11)
+ );
+ });
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.ts
new file mode 100644
index 0000000000..6166a4b410
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/model/route.model.ts
@@ -0,0 +1,443 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/member-ordering */
+
+import { Params } from '@angular/router';
+
+import { BaseRouterStoreState, RouterReducerState } from '@ngrx/router-store';
+
+import { CollectionsUtil, PrimitivesNil } from '../../../utils';
+
+import { Serializable, TaurusRouteData } from '../../../common';
+
+/**
+ * ** Route Segments Class.
+ *
+ *
+ */
+export class RouteSegments {
+ public readonly routePath: string;
+ public readonly data: TaurusRouteData;
+ public readonly params: Params;
+ public readonly queryParams: Params;
+ public readonly parent?: RouteSegments;
+ public readonly configPath?: string;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(
+ routePath: string,
+ data: TaurusRouteData,
+ params: Params,
+ queryParams: Params,
+ parent?: RouteSegments,
+ configPath?: string) {
+
+ this.routePath = routePath ?? '';
+ this.data = data || {};
+ this.params = params || {};
+ this.queryParams = queryParams || {};
+ this.parent = parent;
+ this.configPath = configPath;
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(
+ routePath?: string,
+ data?: TaurusRouteData,
+ params?: Params,
+ queryParams?: Params,
+ parent?: RouteSegments,
+ configPath?: string): RouteSegments {
+
+ return new RouteSegments(routePath, data, params, queryParams, parent, configPath);
+ }
+
+ /**
+ * ** Factory method for empty RouteSegments.
+ */
+ static empty(): RouteSegments {
+ return RouteSegments.of(null, null, null, null, null, null);
+ }
+
+ /**
+ * ** Get RoutePath Segments.
+ */
+ get routePathSegments(): string[] {
+ if (this.parent) {
+ return ([] as string[])
+ .concat(this.parent.routePathSegments, this.routePath)
+ .filter((path) => path);
+ }
+
+ return [this.routePath];
+ }
+
+ /**
+ * ** Get ConfigPath Segments.
+ */
+ get configPathSegments(): string[] {
+ if (this.parent) {
+ return ([] as string[])
+ .concat(this.parent.configPathSegments, this.configPath)
+ .filter((path) => path);
+ }
+
+ return [this.configPath];
+ }
+
+ /**
+ * ** Get Data from Route configuration by key.
+ *
+ * - Return first (closest) found key starting from the current one.
+ */
+ getData(key: string): T {
+ if (this.data[key]) {
+ return this.data[key] as T;
+ }
+
+ if (this.parent) {
+ return this.parent.getData(key);
+ }
+
+ return undefined;
+ }
+
+ /**
+ * ** Get url param by key.
+ *
+ * - Return first (closest) found key starting from the current one.
+ */
+ getParam(key: string): string {
+ if (this.params[key]) {
+ return this.params[key] as string;
+ }
+
+ if (this.parent) {
+ return this.parent.getParam(key);
+ }
+
+ return undefined;
+ }
+
+ /**
+ * ** Get query param by key.
+ */
+ getQueryParam(key: string): string {
+ if (this.queryParams[key]) {
+ return this.queryParams[key] as string;
+ }
+
+ if (this.parent) {
+ return this.parent.getQueryParam(key);
+ }
+
+ return undefined;
+ }
+}
+
+/**
+ * ** Route State Class.
+ *
+ *
+ */
+export class RouteState implements BaseRouterStoreState, Serializable {
+ public readonly routeSegments: RouteSegments;
+ public readonly url: string;
+
+ /**
+ * ** Constructor.
+ */
+ constructor(routeSegments: RouteSegments,
+ url: string) {
+ this.routeSegments = routeSegments ?? RouteSegments.empty();
+ this.url = url ?? '';
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(routeSegments: RouteSegments, url: string): RouteState {
+ return new RouteState(routeSegments, url);
+ }
+
+ /**
+ * ** Factory method for empty State.
+ */
+ static empty(): RouteState {
+ return RouteState.of(null, null);
+ }
+
+ /**
+ * ** Get serialized queryString.
+ */
+ static serializeQueryParams(queryParams: unknown): string {
+ const paramsKeys = Object.keys(queryParams);
+
+ if (!paramsKeys.length) {
+ return '';
+ }
+
+ return paramsKeys
+ .map((key) => `${ encodeURIComponent(key) }=${ encodeURIComponent(queryParams[key] as string) }`)
+ .join('&');
+ }
+
+ /**
+ * ** Returns current RoutePath.
+ */
+ get routePath(): string {
+ return this.routeSegments.routePath;
+ }
+
+ /**
+ * ** Returns current Absolute RoutePath.
+ */
+ get absoluteRoutePath(): string {
+ return RouteState._resolveAbsolutePath(
+ this.routePathSegments
+ );
+ }
+
+ /**
+ * ** Returns the route paths for each route segment starting from the root.
+ */
+ get routePathSegments(): string[] {
+ return this.routeSegments.routePathSegments;
+ }
+
+ /**
+ * ** Returns current ConfigPath.
+ */
+ get configPath(): string {
+ return this.routeSegments.configPath;
+ }
+
+ /**
+ * ** Returns current Absolute ConfigPath.
+ */
+ get absoluteConfigPath(): string {
+ return RouteState._resolveAbsolutePath(
+ this.configPathSegments
+ );
+ }
+
+ /**
+ * ** Returns the config paths for each route segment starting from the root.
+ */
+ get configPathSegments(): string[] {
+ return this.routeSegments.configPathSegments;
+ }
+
+ /**
+ * ** Get all query params.
+ */
+ get queryParams(): Params {
+ return this.routeSegments.queryParams;
+ }
+
+ /**
+ * ** Get serialized queryString.
+ */
+ serializeQueryParams(): string {
+ return RouteState.serializeQueryParams(this.queryParams);
+ }
+
+ /**
+ * ** Get url including QueryParams.
+ */
+ getUrl(): string {
+ return `${ this.absoluteRoutePath }?${ this.serializeQueryParams() }`;
+ }
+
+ /**
+ * ** Get Data from Route configuration by key.
+ *
+ * - Return first (closest) found key starting from first RouteSegment.
+ */
+ getData(key: string): T {
+ return this.routeSegments.getData(key);
+ }
+
+ /**
+ * ** Get url param by key.
+ *
+ * - Return first (closest) found key starting from first RouteSegment.
+ */
+ getParam(key: string): string {
+ return this.routeSegments.getParam(key);
+ }
+
+ /**
+ * ** Get query param by key.
+ */
+ getQueryParam(key: string): string {
+ return this.routeSegments.getQueryParam(key);
+ }
+
+ /**
+ * ** Get Absolute ConfigPath.
+ */
+ getAbsoluteConfigPath(): string {
+ return this.absoluteConfigPath;
+ }
+
+ /**
+ * ** Get parent of current Absolute ConfigPath.
+ */
+ getParentAbsoluteConfigPath(): string {
+ const configPathSegments = this.configPathSegments;
+ configPathSegments.pop();
+
+ return RouteState._resolveAbsolutePath(configPathSegments);
+ }
+
+ /**
+ * ** Get Absolute RoutePath.
+ */
+ getAbsoluteRoutePath(): string {
+ return RouteState._resolveAbsolutePath(
+ this.routePathSegments
+ );
+ }
+
+ /**
+ * ** Get parent of current Absolute RoutePath.
+ */
+ getParentAbsoluteRoutePath(): string {
+ const routePathSegments = this.routePathSegments;
+ routePathSegments.pop();
+
+ return RouteState._resolveAbsolutePath(routePathSegments);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ toJSON(): SerializedRouteState {
+ return {
+ url: this.url,
+ routePath: this.routePath,
+ absoluteRoutePath: this.absoluteRoutePath,
+ routePathSegments: this.routePathSegments,
+ configPath: this.configPath,
+ absoluteConfigPath: this.absoluteConfigPath,
+ configPathSegments: this.configPathSegments,
+ queryParams: this.queryParams,
+ routeSegments: this.routeSegments
+ };
+ }
+
+ /**
+ * ** Resolve Absolute RoutePath from given routePathSegments.
+ */
+ private static _resolveAbsolutePath(routePathSegments: string[]): string {
+ const path = routePathSegments
+ .join('/')
+ .replace(/^\/+/, '');
+
+ if (path === '') {
+ return '/';
+ }
+
+ return `/${ path }`;
+ }
+}
+
+/**
+ * ** Router state.
+ */
+export class RouterState implements RouterReducerState {
+ readonly state: RouteState;
+ readonly navigationId: number;
+ readonly previousStates: RouterState[];
+
+ /**
+ * ** Constructor.
+ */
+ constructor(state: RouteState,
+ navigationId: number) {
+ this.state = state ?? RouteState.empty();
+ this.navigationId = navigationId ?? null;
+ this.previousStates = [];
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of(state: RouteState, navigationId: number): RouterState {
+ return new RouterState(state, navigationId);
+ }
+
+ /**
+ * ** Factory method for empty State.
+ */
+ static empty(): RouterState {
+ return RouterState.of(null, null);
+ }
+
+ /**
+ * ** Returns previous RouterState if exist otherwise null.
+ *
+ * - Optional parameter could be provided to instruct which previous RouterState to return, default one is 0.
+ * - 0 means the first before current.
+ * - 1 means the second before current.
+ * - 2 means the third before current.
+ * - 3 ... etc...
+ */
+ getPrevious(index = 0): RouterState | null {
+ const lookupIndex = CollectionsUtil.isNumber(index)
+ ? index
+ : 0;
+
+ if (lookupIndex >= 0 && lookupIndex < this.previousStates.length) {
+ return this.previousStates[lookupIndex];
+ }
+
+ return null;
+ }
+
+ /**
+ * ** Append previous RouterState[] to current One.
+ *
+ * - Internal API used in reducer, not for public use.
+ */
+ appendPrevious(routerState: RouterState): void {
+ const previousStoredStates: RouterState[] = [...routerState.previousStates];
+ const cleanedPreviousState = RouterState.of(routerState.state, routerState.navigationId);
+
+ if (this.navigationId !== cleanedPreviousState.navigationId) {
+ if (previousStoredStates.length >= 10) {
+ previousStoredStates.pop();
+ }
+
+ previousStoredStates.unshift(cleanedPreviousState);
+ }
+
+ this.previousStates.length = 0;
+ this.previousStates.push(...previousStoredStates);
+ }
+}
+
+/**
+ * ** Route state serialized.
+ */
+interface SerializedRouteState {
+ url: string;
+ routePath: string;
+ absoluteRoutePath: string;
+ routePathSegments: string[];
+ configPath: string;
+ absoluteConfigPath: string;
+ configPathSegments: string[];
+ queryParams: { [key: string]: PrimitivesNil };
+ routeSegments: RouteSegments;
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/public-api.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/public-api.ts
new file mode 100644
index 0000000000..a99e71c6d1
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/public-api.ts
@@ -0,0 +1,9 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './index';
+export * from './factory';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/index.ts
new file mode 100644
index 0000000000..adc9a3f64f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './router.service';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.spec.ts
new file mode 100644
index 0000000000..fde2ad54e6
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.spec.ts
@@ -0,0 +1,193 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* eslint-disable @typescript-eslint/dot-notation */
+
+import { TestBed } from '@angular/core/testing';
+
+import { of } from 'rxjs';
+import { delay } from 'rxjs/operators';
+
+import { marbles } from 'rxjs-marbles/jasmine';
+
+import { Store } from '@ngrx/store';
+
+import { STORE_ROUTER, StoreState } from '../../ngrx';
+
+import { RouterState, RouteState } from '../model';
+
+import { RouterService, RouterServiceImpl } from './router.service';
+
+describe('RouterService -> RouterServiceImpl', () => {
+ let storeStub$: jasmine.SpyObj>;
+ let service: RouterService;
+
+ beforeEach(() => {
+ storeStub$ = jasmine.createSpyObj>('store', ['select']);
+
+ TestBed.configureTestingModule({
+ providers: [
+ { provide: Store, useValue: storeStub$ },
+ { provide: RouterService, useClass: RouterServiceImpl }
+ ]
+ });
+
+ service = TestBed.inject(RouterService);
+ });
+
+ it('should verify instance is created', () => {
+ // Then
+ expect(service).toBeDefined();
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|get|', () => {
+ it('should verify will return RouterState', () => {
+ // When
+ const routerState = RouterService.get();
+
+ // Then
+ expect(routerState.state).toEqual(RouteState.empty());
+ });
+ });
+
+ describe('|getState|', () => {
+ it('should verify will return RouteState', () => {
+ // When
+ const routeState = RouterService.getState();
+
+ // Then
+ expect(routeState).toEqual(RouteState.empty());
+ });
+ });
+ });
+ });
+
+ describe('Methods::', () => {
+ describe('|get|', () => {
+ it('should verify will select RouterState from Store', () => {
+ // Given
+ storeStub$.select.and.returnValue(of({ state: {}, navigationId: 5 } as RouterState));
+
+ // When
+ service.get();
+
+ // Then
+ expect(storeStub$.select.calls.mostRecent().args).toEqual([STORE_ROUTER]);
+ });
+
+ it('should verify will return correct Observable state', marbles((m) => {
+ // Given
+ const storeStream$ = m.cold('----a---', {
+ a: {
+ state: RouteState.empty(),
+ navigationId: 7
+ } as RouterState
+ });
+ const expectedOutputStream$ = m.cold('----a---', {
+ a: {
+ state: RouteState.empty(),
+ navigationId: 7
+ } as RouterState
+ });
+ storeStub$.select.and.returnValue(storeStream$);
+
+ // When
+ const response$ = service.get();
+
+ // Then
+ m.expect(response$).toBeObservable(expectedOutputStream$);
+ }));
+ });
+
+ describe('|getState|', () => {
+ it('should verify will select RouterState from Store', () => {
+ // Given
+ storeStub$.select.and.returnValue(of({ state: {}, navigationId: 5 } as RouterState));
+
+ // When
+ service.getState();
+
+ // Then
+ expect(storeStub$.select.calls.mostRecent().args).toEqual([STORE_ROUTER]);
+ });
+
+ it('should verify will return correct Observable state', marbles((m) => {
+ // Given
+ const storeStream$ = m.cold('----a---', {
+ a: {
+ state: RouteState.empty(),
+ navigationId: 7
+ } as RouterState
+ });
+ const expectedOutputStream$ = m.cold('----a---', {
+ a: RouteState.empty()
+ });
+ storeStub$.select.and.returnValue(storeStream$);
+
+ // When
+ const response$ = service.getState();
+
+ // Then
+ m.expect(response$).toBeObservable(expectedOutputStream$);
+ }));
+ });
+
+ describe('|initialize|', () => {
+ it('should verify will push created subscriptions to buffer', () => {
+ // Given
+ const storeStream$ = of({
+ state: RouteState.empty(),
+ navigationId: 7
+ } as RouterState);
+ storeStub$.select.and.returnValue(storeStream$);
+ // @ts-ignore
+ const cleanSubSpy = spyOn(service, 'cleanSubscriptions').and.callThrough();
+
+ // Then 1
+ expect(service['subscriptions'].length).toEqual(0);
+
+ // When
+ service.initialize();
+
+ // Then 2
+ expect(service['subscriptions'].length).toEqual(1);
+ expect(cleanSubSpy).toHaveBeenCalled();
+ });
+
+ it('should verify will create subscriptions and will assign value to local state', marbles((m) => {
+ // Given
+ const routerState = RouterState.of(
+ RouteState.empty(),
+ 7
+ );
+ const storeStream$ = m.hot('-^--a---', {
+ a: routerState
+ });
+ storeStub$.select.and.returnValue(storeStream$);
+
+ // When 1
+ service.initialize();
+
+ // delay of 4 frames
+ of(true).pipe(
+ delay(m.time('----|'))
+ ).subscribe(() => {
+ // Then 1
+ expect(RouterService['_routerState']).toBe(routerState);
+
+ // When 2 // destroy Service
+ service.ngOnDestroy();
+
+ // Then
+ m.expect(storeStream$).toHaveSubscriptions('^---!');
+ });
+ }));
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.ts
new file mode 100644
index 0000000000..2b4af4d298
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/services/router.service.ts
@@ -0,0 +1,99 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Directive, Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs';
+import { map } from 'rxjs/operators';
+
+import { Store } from '@ngrx/store';
+
+import { TaurusObject } from '../../../common';
+
+import { STORE_ROUTER, StoreState } from '../../ngrx';
+
+import { RouterState, RouteState } from '../model';
+
+/**
+ * ** Router Service.
+ *
+ *
+ */
+@Directive()
+// eslint-disable-next-line @angular-eslint/directive-class-suffix
+export abstract class RouterService extends TaurusObject {
+ protected static _routerState: RouterState = RouterState.empty();
+
+ /**
+ * ** Will return current Router.
+ */
+ static get(): RouterState {
+ return RouterService._routerState;
+ }
+
+ /**
+ * ** Will return current Route State.
+ */
+ static getState(): RouteState {
+ return RouterService._routerState.state;
+ }
+
+ /**
+ * ** Will return Observable with NgRx Route State.
+ */
+ abstract get(): Observable;
+
+ /**
+ * ** Will return Observable with Route State.
+ */
+ abstract getState(): Observable;
+
+ /**
+ * ** Will initialize service.
+ */
+ abstract initialize(): void;
+}
+
+/**
+ * @inheritDoc
+ */
+@Injectable()
+export class RouterServiceImpl extends RouterService {
+ constructor(private readonly store$: Store) {
+ super();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ get(): Observable {
+ return this.store$.select(STORE_ROUTER);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ getState(): Observable {
+ return this.get().pipe(
+ map((data) => data.state)
+ );
+ }
+
+ /**
+ * @inheritDoc
+ */
+ initialize(): void {
+ this.cleanSubscriptions();
+
+ this.subscriptions.push(
+ this.get()
+ .subscribe((state) => {
+ RouterService._routerState = state;
+ })
+ );
+ }
+}
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/index.ts
new file mode 100644
index 0000000000..258264b723
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './router.actions';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.spec.ts
new file mode 100644
index 0000000000..626c0f6ba0
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.spec.ts
@@ -0,0 +1,171 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { BaseAction, BaseActionWithPayload } from '../../../ngrx';
+
+import {
+ LOCATION_BACK,
+ LOCATION_FORWARD,
+ LOCATION_GO,
+ LocationBack,
+ LocationForward,
+ LocationGo,
+ ROUTER_NAVIGATE,
+ RouterNavigate
+} from './router.actions';
+
+describe('NavigationActions', () => {
+ describe('RouterNavigate', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new RouterNavigate({ commands: [], extras: {} })).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new RouterNavigate({ commands: [], extras: {} });
+
+ // Then
+ expect(instance.type).toEqual(ROUTER_NAVIGATE);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new RouterNavigate({ commands: [], extras: {} });
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = RouterNavigate.of({ commands: [], extras: {} });
+
+ // Then
+ expect(instance).toBeInstanceOf(RouterNavigate);
+ });
+ });
+ });
+ });
+ });
+
+ describe('LocationGo', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new LocationGo({ path: 'entity/15', query: 'search=test-team', state: {} })).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new LocationGo({ path: 'entity/15', query: 'search=test-team', state: {} });
+
+ // Then
+ expect(instance.type).toEqual(LOCATION_GO);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new LocationGo({ path: 'entity/15', query: 'search=test-team', state: {} });
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseActionWithPayload);
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = LocationGo.of({ path: 'entity/15', query: 'search=test-team', state: {} });
+
+ // Then
+ expect(instance).toBeInstanceOf(LocationGo);
+ });
+ });
+ });
+ });
+ });
+
+ describe('LocationBack', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new LocationBack()).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new LocationBack();
+
+ // Then
+ expect(instance.type).toEqual(LOCATION_BACK);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new LocationBack();
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = LocationBack.of();
+
+ // Then
+ expect(instance).toBeInstanceOf(LocationBack);
+ });
+ });
+ });
+ });
+ });
+
+ describe('LocationForward', () => {
+ it('should verify instance is created', () => {
+ // Then
+ expect(() => new LocationForward()).toBeDefined();
+ });
+
+ it('should verify correct type is assigned', () => {
+ // When
+ const instance = new LocationForward();
+
+ // Then
+ expect(instance.type).toEqual(LOCATION_FORWARD);
+ });
+
+ it('should verify prototype chaining', () => {
+ // When
+ const instance = new LocationForward();
+
+ // Then
+ expect(instance).toBeInstanceOf(BaseAction);
+ });
+
+ describe('Statics::', () => {
+ describe('Methods::', () => {
+ describe('|of|', () => {
+ it('should verify factory method will create instance', () => {
+ // When
+ const instance = LocationForward.of();
+
+ // Then
+ expect(instance).toBeInstanceOf(LocationForward);
+ });
+ });
+ });
+ });
+ });
+});
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.ts
new file mode 100644
index 0000000000..299d347a31
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/actions/router.actions.ts
@@ -0,0 +1,125 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { NavigationExtras } from '@angular/router';
+
+import { BaseAction, BaseActionWithPayload } from '../../../ngrx/actions';
+
+/**
+ * ** Action Identifier for Router Navigate.
+ *
+ *
+ */
+export const ROUTER_NAVIGATE = '[router] Navigate';
+
+/**
+ * ** Action Identifier for Location Go.
+ *
+ *
+ */
+export const LOCATION_GO = '[location] Go';
+
+/**
+ * ** Action Identifier for Location Back.
+ *
+ *
+ */
+export const LOCATION_BACK = '[location] Back';
+
+/**
+ * ** Action Identifier for Location Forward.
+ *
+ *
+ */
+export const LOCATION_FORWARD = '[location] Forward';
+
+export interface NavigatePayload {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ commands: any[];
+ extras?: NavigationExtras;
+}
+
+/**
+ * ** Navigate Action instruct subscribers that they should navigate to given path.
+ *
+ *
+ */
+export class RouterNavigate extends BaseActionWithPayload {
+ constructor(payload: NavigatePayload) {
+ super(ROUTER_NAVIGATE, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: NavigatePayload) {
+ return new RouterNavigate(payload);
+ }
+}
+
+export interface GoPayload {
+ path: string;
+ query?: string;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ state?: any;
+}
+
+/**
+ * ** Location Go Action instruct subscribers that they should navigate using Location.
+ *
+ *
+ */
+export class LocationGo extends BaseActionWithPayload {
+ constructor(payload: GoPayload) {
+ super(LOCATION_GO, payload);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static override of(payload: GoPayload) {
+ return new LocationGo(payload);
+ }
+}
+
+/**
+ * ** Back Action instruct subscribers to pop history Backward.
+ *
+ *
+ */
+export class LocationBack extends BaseAction {
+ constructor() {
+ super(LOCATION_BACK);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of() {
+ return new LocationBack();
+ }
+}
+
+/**
+ * ** Forward Action instruct subscribers to go Forward.
+ *
+ *
+ */
+export class LocationForward extends BaseAction {
+ constructor() {
+ super(LOCATION_FORWARD);
+ }
+
+ /**
+ * ** Factory method.
+ */
+ static of() {
+ return new LocationForward();
+ }
+}
+
+export type NavigationActions = RouterNavigate | LocationGo | LocationBack | LocationForward;
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/index.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/index.ts
new file mode 100644
index 0000000000..49fe38a24f
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/index.ts
@@ -0,0 +1,8 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+export * from './router.effects';
diff --git a/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/router.effects.spec.ts b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/router.effects.spec.ts
new file mode 100644
index 0000000000..e3d34b49a5
--- /dev/null
+++ b/projects/frontend/shared-components/gui/projects/shared/src/lib/core/router/state/effects/router.effects.spec.ts
@@ -0,0 +1,129 @@
+
+
+/*
+ * Copyright 2021-2023 VMware, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+import { Location } from '@angular/common';
+import { Router } from '@angular/router';
+import { TestBed, waitForAsync } from '@angular/core/testing';
+
+import { provideMockActions } from '@ngrx/effects/testing';
+
+import { Observable } from 'rxjs';
+import { marbles } from 'rxjs-marbles/jasmine';
+
+import { RouterEffects } from './router.effects';
+import { LocationBack, LocationForward, LocationGo, RouterNavigate } from '../actions';
+
+describe('RouterEffects', () => {
+ let effects: RouterEffects;
+
+ let routeAction$: Observable;
+
+ let routerStub: jasmine.SpyObj;
+ let locationStub: jasmine.SpyObj;
+
+ beforeEach(waitForAsync(() => {
+ routerStub = jasmine.createSpyObj('router', ['navigate']);
+ locationStub = jasmine.createSpyObj