diff --git a/APP/LICENSE.md b/APP/LICENSE.md
new file mode 100644
index 0000000..8b6c2ef
--- /dev/null
+++ b/APP/LICENSE.md
@@ -0,0 +1,11 @@
+# Page Templates and Building Blocks
+
+This package is part of the [Telerik and Kendo UI Accelerator](https://www.telerik.com/page-templates-and-ui-blocks) add-on.
+
+## License
+
+This is commercial software. To use it, you need to agree to the [**End User License Agreement for Progress® Telerik and Kendo UI Accelerator**](https://www.telerik.com/purchase/license-agreement/ui-accelerator).
+
+All available Progress® Telerik and Kendo UI Accelerator commercial licenses may be obtained at the [Progress® Telerik and Kendo UI Accelerator website](https://www.telerik.com/purchase.aspx?filter=ui-accelerator#individual-products).
+
+*Copyright © 2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.*
diff --git a/APP/angular.json b/APP/angular.json
new file mode 100644
index 0000000..810c586
--- /dev/null
+++ b/APP/angular.json
@@ -0,0 +1,116 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "version": 1,
+ "newProjectRoot": "projects",
+ "projects": {
+ "client-bridge": {
+ "projectType": "application",
+ "schematics": {
+ "@schematics/angular:class": {
+ "skipTests": true
+ },
+ "@schematics/angular:component": {
+ "skipTests": true
+ },
+ "@schematics/angular:directive": {
+ "skipTests": true
+ },
+ "@schematics/angular:guard": {
+ "skipTests": true
+ },
+ "@schematics/angular:interceptor": {
+ "skipTests": true
+ },
+ "@schematics/angular:pipe": {
+ "skipTests": true
+ },
+ "@schematics/angular:resolver": {
+ "skipTests": true
+ },
+ "@schematics/angular:service": {
+ "skipTests": true
+ }
+ },
+ "root": "",
+ "sourceRoot": "src",
+ "prefix": "app",
+ "architect": {
+ "build": {
+ "builder": "@angular/build:application",
+ "options": {
+ "browser": "src/main.ts",
+ "polyfills": [
+ "zone.js",
+ "@angular/localize/init"
+ ],
+ "tsConfig": "tsconfig.app.json",
+ "assets": [
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.scss"
+ ]
+ },
+ "configurations": {
+ "production": {
+ "budgets": [
+ {
+ "type": "initial",
+ "maximumWarning": "500kB",
+ "maximumError": "5mb"
+ },
+ {
+ "type": "anyComponentStyle",
+ "maximumWarning": "4kB",
+ "maximumError": "1mb"
+ }
+ ],
+ "outputHashing": "all"
+ },
+ "development": {
+ "optimization": false,
+ "extractLicenses": false,
+ "sourceMap": true
+ }
+ },
+ "defaultConfiguration": "production"
+ },
+ "serve": {
+ "builder": "@angular/build:dev-server",
+ "options": {
+ "buildTarget": "client-bridge:build"
+ },
+ "configurations": {
+ "production": {
+ "buildTarget": "client-bridge:build:production"
+ },
+ "development": {
+ "buildTarget": "client-bridge:build:development"
+ }
+ },
+ "defaultConfiguration": "development"
+ },
+ "extract-i18n": {
+ "builder": "@angular/build:extract-i18n"
+ },
+ "test": {
+ "builder": "@angular/build:karma",
+ "options": {
+ "polyfills": [
+ "zone.js",
+ "zone.js/testing",
+ "@angular/localize/init"
+ ],
+ "tsConfig": "tsconfig.spec.json",
+ "assets": [
+ "src/assets"
+ ],
+ "styles": [
+ "src/styles.scss"
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/APP/email-template.html b/APP/email-template.html
new file mode 100644
index 0000000..19f1033
--- /dev/null
+++ b/APP/email-template.html
@@ -0,0 +1,387 @@
+
+
+
+
+
+
+ Escrow Portal Access - RBJ Identity
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hello [Client Name],
+
+
+
+ Your escrow transaction is ready for review. Please use the secure link below to access
+ your
+ personalized portal where you can view transaction details, upload required documents,
+ and communicate
+ with your escrow officer.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Company:
+ [Company
+ Name]
+
+
+
+
+ Escrow
+ Officer:
+ [Escrow
+ Officer Name]
+
+
+
+
+ Property:
+ [Property
+ Address]
+
+
+
+
+ Transaction
+ ID:
+ [Transaction
+ ID]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ This link is secure and personalized for your transaction. Please do not
+ share this link with
+ others. If you did not request this access or have any concerns, please
+ contact your escrow officer
+ immediately.
+
+
+
+
+
+
+ If you have any questions or need assistance, please don't hesitate to contact your
+ escrow officer
+ directly. We're here to help make your transaction as smooth as possible.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/package-lock.json b/APP/package-lock.json
new file mode 100644
index 0000000..c747765
--- /dev/null
+++ b/APP/package-lock.json
@@ -0,0 +1,11376 @@
+{
+ "name": "client-bridge",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "client-bridge",
+ "version": "0.0.0",
+ "dependencies": {
+ "@angular/animations": "^20.1.0",
+ "@angular/common": "^20.1.0",
+ "@angular/compiler": "^20.1.0",
+ "@angular/core": "^20.1.0",
+ "@angular/forms": "^20.1.0",
+ "@angular/localize": "^20.1.6",
+ "@angular/platform-browser": "^20.1.0",
+ "@angular/router": "^20.1.0",
+ "@progress/kendo-angular-buttons": "^20.0.0",
+ "@progress/kendo-angular-charts": "^20.0.0",
+ "@progress/kendo-angular-common": "^20.0.0",
+ "@progress/kendo-angular-conversational-ui": "^20.0.0",
+ "@progress/kendo-angular-dateinputs": "^20.0.0",
+ "@progress/kendo-angular-dialog": "^20.0.0",
+ "@progress/kendo-angular-dropdowns": "^20.0.0",
+ "@progress/kendo-angular-editor": "^20.0.0",
+ "@progress/kendo-angular-excel-export": "^20.0.3",
+ "@progress/kendo-angular-gauges": "^20.0.0",
+ "@progress/kendo-angular-grid": "^20.0.0",
+ "@progress/kendo-angular-icons": "^20.0.0",
+ "@progress/kendo-angular-indicators": "^20.0.0",
+ "@progress/kendo-angular-inputs": "^20.0.0",
+ "@progress/kendo-angular-intl": "^20.0.0",
+ "@progress/kendo-angular-l10n": "^20.0.0",
+ "@progress/kendo-angular-label": "^20.0.0",
+ "@progress/kendo-angular-layout": "^20.0.0",
+ "@progress/kendo-angular-listview": "^20.0.0",
+ "@progress/kendo-angular-map": "^20.0.0",
+ "@progress/kendo-angular-menu": "^20.0.0",
+ "@progress/kendo-angular-navigation": "^20.0.0",
+ "@progress/kendo-angular-pager": "^20.0.0",
+ "@progress/kendo-angular-pdf-export": "^20.0.3",
+ "@progress/kendo-angular-popup": "^20.0.0",
+ "@progress/kendo-angular-progressbar": "^20.0.0",
+ "@progress/kendo-angular-scrollview": "^20.0.0",
+ "@progress/kendo-angular-toolbar": "^20.0.3",
+ "@progress/kendo-angular-tooltip": "^20.0.0",
+ "@progress/kendo-angular-treeview": "^20.0.0",
+ "@progress/kendo-angular-upload": "^20.0.0",
+ "@progress/kendo-angular-utils": "^20.0.0",
+ "@progress/kendo-data-query": "^1.7.1",
+ "@progress/kendo-drawing": "^1.22.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "@progress/kendo-svg-icons": "^4.5.0",
+ "@progress/kendo-theme-default": "^12.0.0",
+ "@progress/kendo-theme-utils": "^12.0.0",
+ "rxjs": "~7.8.0",
+ "tslib": "^2.3.0",
+ "zone.js": "~0.15.0"
+ },
+ "devDependencies": {
+ "@angular/build": "^20.1.6",
+ "@angular/cli": "^20.1.6",
+ "@angular/compiler-cli": "^20.1.0",
+ "@angular/localize": "^20.2.1",
+ "@types/jasmine": "~5.1.0",
+ "jasmine-core": "~5.8.0",
+ "karma": "~6.4.0",
+ "karma-chrome-launcher": "~3.2.0",
+ "karma-coverage": "~2.2.0",
+ "karma-jasmine": "~5.1.0",
+ "karma-jasmine-html-reporter": "~2.1.0",
+ "typescript": "~5.8.2"
+ }
+ },
+ "node_modules/@algolia/abtesting": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.1.0.tgz",
+ "integrity": "sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-abtesting": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.35.0.tgz",
+ "integrity": "sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-analytics": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.35.0.tgz",
+ "integrity": "sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-common": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.35.0.tgz",
+ "integrity": "sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-insights": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.35.0.tgz",
+ "integrity": "sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-personalization": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.35.0.tgz",
+ "integrity": "sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-query-suggestions": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.35.0.tgz",
+ "integrity": "sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/client-search": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.35.0.tgz",
+ "integrity": "sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/ingestion": {
+ "version": "1.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.35.0.tgz",
+ "integrity": "sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/monitoring": {
+ "version": "1.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.35.0.tgz",
+ "integrity": "sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/recommend": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.35.0.tgz",
+ "integrity": "sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-browser-xhr": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.35.0.tgz",
+ "integrity": "sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-fetch": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.35.0.tgz",
+ "integrity": "sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@algolia/requester-node-http": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.35.0.tgz",
+ "integrity": "sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/client-common": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@angular-devkit/architect": {
+ "version": "0.2002.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2002.0.tgz",
+ "integrity": "sha512-PaBXFP1kdUuNtMie0lWnitlYbq8o1gz/s0YIa8oY1X3swOJ7bP6kBfxTb9opV5uXAOkXg2zCdnZ4Eu1aVkgPGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "20.2.0",
+ "rxjs": "7.8.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular-devkit/core": {
+ "version": "20.2.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.2.0.tgz",
+ "integrity": "sha512-3CM6Zsr09Kf92ItFkxijlnC4+ZOgkxdCk0vFYvuw9UuvTDNwyIqJi6693PRPRbcXgpdY2vs6u99elSvQVmoEEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "8.17.1",
+ "ajv-formats": "3.0.1",
+ "jsonc-parser": "3.3.1",
+ "picomatch": "4.0.3",
+ "rxjs": "7.8.2",
+ "source-map": "0.7.6"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "peerDependencies": {
+ "chokidar": "^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "chokidar": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular-devkit/schematics": {
+ "version": "20.2.0",
+ "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.2.0.tgz",
+ "integrity": "sha512-TCPIN6Bd04oGuNocETmsd9hzGYrjrivisbMKb0WOuDi3OnCkmWqsPR+QA2kYwTOGqG3HXkz/z3CA0g04M2fgrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "20.2.0",
+ "jsonc-parser": "3.3.1",
+ "magic-string": "0.30.17",
+ "ora": "8.2.0",
+ "rxjs": "7.8.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular/animations": {
+ "version": "20.3.4",
+ "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.4.tgz",
+ "integrity": "sha512-b+vFsTtMYtOrcZZLXB4BxuErbrLlShFT6khTvkwu/pFK8ri3tasyJGkeKRZJHao5ZsWdZSqV2mRwzg7vphchnA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "20.3.4"
+ }
+ },
+ "node_modules/@angular/build": {
+ "version": "20.2.0",
+ "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.2.0.tgz",
+ "integrity": "sha512-/Yhqhg01UvX0E+tx4WAeK3AnwpZLqcw+XKTmsPsH5rbqpLKNRR9XsC3PJ4qBFU1u9/Lh13mmmr1+pG2p8ixMug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "2.3.0",
+ "@angular-devkit/architect": "0.2002.0",
+ "@babel/core": "7.28.3",
+ "@babel/helper-annotate-as-pure": "7.27.3",
+ "@babel/helper-split-export-declaration": "7.24.7",
+ "@inquirer/confirm": "5.1.14",
+ "@vitejs/plugin-basic-ssl": "2.1.0",
+ "beasties": "0.3.5",
+ "browserslist": "^4.23.0",
+ "esbuild": "0.25.9",
+ "https-proxy-agent": "7.0.6",
+ "istanbul-lib-instrument": "6.0.3",
+ "jsonc-parser": "3.3.1",
+ "listr2": "9.0.1",
+ "magic-string": "0.30.17",
+ "mrmime": "2.0.1",
+ "parse5-html-rewriting-stream": "8.0.0",
+ "picomatch": "4.0.3",
+ "piscina": "5.1.3",
+ "rolldown": "1.0.0-beta.32",
+ "sass": "1.90.0",
+ "semver": "7.7.2",
+ "source-map-support": "0.5.21",
+ "tinyglobby": "0.2.14",
+ "vite": "7.1.2",
+ "watchpack": "2.4.4"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ },
+ "optionalDependencies": {
+ "lmdb": "3.4.2"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "^20.0.0",
+ "@angular/compiler-cli": "^20.0.0",
+ "@angular/core": "^20.0.0",
+ "@angular/localize": "^20.0.0",
+ "@angular/platform-browser": "^20.0.0",
+ "@angular/platform-server": "^20.0.0",
+ "@angular/service-worker": "^20.0.0",
+ "@angular/ssr": "^20.2.0",
+ "karma": "^6.4.0",
+ "less": "^4.2.0",
+ "ng-packagr": "^20.0.0",
+ "postcss": "^8.4.0",
+ "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0",
+ "tslib": "^2.3.0",
+ "typescript": ">=5.8 <6.0",
+ "vitest": "^3.1.1"
+ },
+ "peerDependenciesMeta": {
+ "@angular/core": {
+ "optional": true
+ },
+ "@angular/localize": {
+ "optional": true
+ },
+ "@angular/platform-browser": {
+ "optional": true
+ },
+ "@angular/platform-server": {
+ "optional": true
+ },
+ "@angular/service-worker": {
+ "optional": true
+ },
+ "@angular/ssr": {
+ "optional": true
+ },
+ "karma": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "ng-packagr": {
+ "optional": true
+ },
+ "postcss": {
+ "optional": true
+ },
+ "tailwindcss": {
+ "optional": true
+ },
+ "vitest": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/cli": {
+ "version": "20.2.0",
+ "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.2.0.tgz",
+ "integrity": "sha512-p62hkuQOxf5kJsVq6AT7B1MHYo1uPGoZV4lf47qOrLjl0WANwfxEgLvyuVgL47ylnINbPnITeeUdoadVn4t1sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/architect": "0.2002.0",
+ "@angular-devkit/core": "20.2.0",
+ "@angular-devkit/schematics": "20.2.0",
+ "@inquirer/prompts": "7.8.2",
+ "@listr2/prompt-adapter-inquirer": "3.0.1",
+ "@modelcontextprotocol/sdk": "1.17.3",
+ "@schematics/angular": "20.2.0",
+ "@yarnpkg/lockfile": "1.1.0",
+ "algoliasearch": "5.35.0",
+ "ini": "5.0.0",
+ "jsonc-parser": "3.3.1",
+ "listr2": "9.0.1",
+ "npm-package-arg": "13.0.0",
+ "pacote": "21.0.0",
+ "resolve": "1.22.10",
+ "semver": "7.7.2",
+ "yargs": "18.0.0",
+ "zod": "3.25.76"
+ },
+ "bin": {
+ "ng": "bin/ng.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@angular/common": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.2.1.tgz",
+ "integrity": "sha512-T6RYnDZA9TyYhj2hUz4set8p4RbBCg6IKUvy6qzdKTl4nn4xQ0XUV7aGBYN4LKiGrse9lzlVUAyXtkhmwuBbCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/core": "20.2.1",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/compiler": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.2.1.tgz",
+ "integrity": "sha512-ghVt1E8xmwjMwqyGRwXYJkr7fz40VEreUSX1q+gEzbGTftVrK1foxPT8jcueIn0ztArDf7+zSMtu314FiJZyYA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@angular/compiler-cli": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.2.1.tgz",
+ "integrity": "sha512-VpbcRqNPJvy1L9RDtGGQsQiOrMzxodUWklphbtnh9MrrK6lLuy6Qj2ROiW7vKL9WfLTCXWA24gBAcMAR76dq3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "7.28.3",
+ "@jridgewell/sourcemap-codec": "^1.4.14",
+ "chokidar": "^4.0.0",
+ "convert-source-map": "^1.5.1",
+ "reflect-metadata": "^0.2.0",
+ "semver": "^7.0.0",
+ "tslib": "^2.3.0",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "ng-xi18n": "bundles/src/bin/ng_xi18n.js",
+ "ngc": "bundles/src/bin/ngc.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "20.2.1",
+ "typescript": ">=5.8 <6.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/core": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.2.1.tgz",
+ "integrity": "sha512-/hl3AkmdQ62P9ttmfULEDg9GIz7BkzhGv9bSH2ssiU3Y4ax6eM8uQXEbMxBA8OUKOvg1Q4POcNHIiJQgO5t28Q==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "20.2.1",
+ "rxjs": "^6.5.3 || ^7.4.0",
+ "zone.js": "~0.15.0"
+ },
+ "peerDependenciesMeta": {
+ "@angular/compiler": {
+ "optional": true
+ },
+ "zone.js": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/forms": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.2.1.tgz",
+ "integrity": "sha512-SfkiHEIFPLtTKeaXUTpRfYnpJDxaeKiTi0YqfvzEjKE68qH0t+pQ4rL0Poch2/l4snP6JS1XzO/nDve1dk3vZw==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "20.2.1",
+ "@angular/core": "20.2.1",
+ "@angular/platform-browser": "20.2.1",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@angular/localize": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-20.2.1.tgz",
+ "integrity": "sha512-vemzYcHt6YX4FutpgNXiXTpKCMVaJdOG/m2+oJyvnr8KvdlrJKczXraPVY4ER+WJiHC5IQSg24otdSFc0UH2JA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "7.28.3",
+ "@types/babel__core": "7.20.5",
+ "tinyglobby": "^0.2.12",
+ "yargs": "^18.0.0"
+ },
+ "bin": {
+ "localize-extract": "tools/bundles/src/extract/cli.js",
+ "localize-migrate": "tools/bundles/src/migrate/cli.js",
+ "localize-translate": "tools/bundles/src/translate/cli.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/compiler": "20.2.1",
+ "@angular/compiler-cli": "20.2.1"
+ }
+ },
+ "node_modules/@angular/platform-browser": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.2.1.tgz",
+ "integrity": "sha512-oxDih/A8G7W+I6oAip+sev+kebioYmzhB/NMzF8C8zx/ieVDzatJ+YeEZQt7eDaJLH94S4sIC25SPq3OFIabxg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/animations": "20.2.1",
+ "@angular/common": "20.2.1",
+ "@angular/core": "20.2.1"
+ },
+ "peerDependenciesMeta": {
+ "@angular/animations": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@angular/router": {
+ "version": "20.2.1",
+ "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.2.1.tgz",
+ "integrity": "sha512-f8KfG55EVnFDC9ud+MbxAP6voKi7hVQH4YaqPK0Lm6pyc1Xp0I5W25iRbg+Y1rO1csHKHauBPkUEESEuVGBGqg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.3.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ },
+ "peerDependencies": {
+ "@angular/common": "20.2.1",
+ "@angular/core": "20.2.1",
+ "@angular/platform-browser": "20.2.1",
+ "rxjs": "^6.5.3 || ^7.4.0"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
+ "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz",
+ "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz",
+ "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.2.0",
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-compilation-targets": "^7.27.2",
+ "@babel/helper-module-transforms": "^7.28.3",
+ "@babel/helpers": "^7.28.3",
+ "@babel/parser": "^7.28.3",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz",
+ "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "@babel/types": "^7.28.2",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz",
+ "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.27.2",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
+ "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz",
+ "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1",
+ "@babel/traverse": "^7.28.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-split-export-declaration": {
+ "version": "7.24.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz",
+ "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.24.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz",
+ "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz",
+ "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz",
+ "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/parser": "^7.27.2",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.28.3",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz",
+ "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/generator": "^7.28.3",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.28.3",
+ "@babel/template": "^7.27.2",
+ "@babel/types": "^7.28.2",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.28.2",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz",
+ "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
+ "node_modules/@emnapi/core": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.5.tgz",
+ "integrity": "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/wasi-threads": "1.0.4",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz",
+ "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@emnapi/wasi-threads": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.4.tgz",
+ "integrity": "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz",
+ "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz",
+ "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz",
+ "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz",
+ "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz",
+ "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz",
+ "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz",
+ "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz",
+ "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz",
+ "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz",
+ "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz",
+ "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz",
+ "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz",
+ "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz",
+ "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz",
+ "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz",
+ "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz",
+ "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz",
+ "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz",
+ "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz",
+ "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz",
+ "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz",
+ "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz",
+ "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@inquirer/checkbox": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.2.tgz",
+ "integrity": "sha512-E+KExNurKcUJJdxmjglTl141EwxWyAHplvsYJQgSwXf8qiNWkTxTuCCqmhFEmbIXd4zLaGMfQFJ6WrZ7fSeV3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/figures": "^1.0.13",
+ "@inquirer/type": "^3.0.8",
+ "ansi-escapes": "^4.3.2",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/confirm": {
+ "version": "5.1.14",
+ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz",
+ "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.1.15",
+ "@inquirer/type": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/core": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.0.tgz",
+ "integrity": "sha512-NyDSjPqhSvpZEMZrLCYUquWNl+XC/moEcVFqS55IEYIYsY0a1cUCevSqk7ctOlnm/RaSBU5psFryNlxcmGrjaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/figures": "^1.0.13",
+ "@inquirer/type": "^3.0.8",
+ "ansi-escapes": "^4.3.2",
+ "cli-width": "^4.1.0",
+ "mute-stream": "^2.0.0",
+ "signal-exit": "^4.1.0",
+ "wrap-ansi": "^6.2.0",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/editor": {
+ "version": "4.2.18",
+ "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.18.tgz",
+ "integrity": "sha512-yeQN3AXjCm7+Hmq5L6Dm2wEDeBRdAZuyZ4I7tWSSanbxDzqM0KqzoDbKM7p4ebllAYdoQuPJS6N71/3L281i6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/external-editor": "^1.0.1",
+ "@inquirer/type": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/expand": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.18.tgz",
+ "integrity": "sha512-xUjteYtavH7HwDMzq4Cn2X4Qsh5NozoDHCJTdoXg9HfZ4w3R6mxV1B9tL7DGJX2eq/zqtsFjhm0/RJIMGlh3ag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/type": "^3.0.8",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.1.tgz",
+ "integrity": "sha512-Oau4yL24d2B5IL4ma4UpbQigkVhzPDXLoqy1ggK4gnHg/stmkffJE4oOXHXF3uz0UEpywG68KcyXsyYpA1Re/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.0",
+ "iconv-lite": "^0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/figures": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz",
+ "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@inquirer/input": {
+ "version": "4.2.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.2.tgz",
+ "integrity": "sha512-hqOvBZj/MhQCpHUuD3MVq18SSoDNHy7wEnQ8mtvs71K8OPZVXJinOzcvQna33dNYLYE4LkA9BlhAhK6MJcsVbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/type": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/number": {
+ "version": "3.0.18",
+ "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.18.tgz",
+ "integrity": "sha512-7exgBm52WXZRczsydCVftozFTrrwbG5ySE0GqUd2zLNSBXyIucs2Wnm7ZKLe/aUu6NUg9dg7Q80QIHCdZJiY4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/type": "^3.0.8"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/password": {
+ "version": "4.0.18",
+ "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.18.tgz",
+ "integrity": "sha512-zXvzAGxPQTNk/SbT3carAD4Iqi6A2JS2qtcqQjsL22uvD+JfQzUrDEtPjLL7PLn8zlSNyPdY02IiQjzoL9TStA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/type": "^3.0.8",
+ "ansi-escapes": "^4.3.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/prompts": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.2.tgz",
+ "integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/checkbox": "^4.2.1",
+ "@inquirer/confirm": "^5.1.14",
+ "@inquirer/editor": "^4.2.17",
+ "@inquirer/expand": "^4.0.17",
+ "@inquirer/input": "^4.2.1",
+ "@inquirer/number": "^3.0.17",
+ "@inquirer/password": "^4.0.17",
+ "@inquirer/rawlist": "^4.1.5",
+ "@inquirer/search": "^3.1.0",
+ "@inquirer/select": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/rawlist": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.6.tgz",
+ "integrity": "sha512-KOZqa3QNr3f0pMnufzL7K+nweFFCCBs6LCXZzXDrVGTyssjLeudn5ySktZYv1XiSqobyHRYYK0c6QsOxJEhXKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/type": "^3.0.8",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/search": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.1.tgz",
+ "integrity": "sha512-TkMUY+A2p2EYVY3GCTItYGvqT6LiLzHBnqsU1rJbrpXUijFfM6zvUx0R4civofVwFCmJZcKqOVwwWAjplKkhxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/figures": "^1.0.13",
+ "@inquirer/type": "^3.0.8",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/select": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.2.tgz",
+ "integrity": "sha512-nwous24r31M+WyDEHV+qckXkepvihxhnyIaod2MG7eCE6G0Zm/HUF6jgN8GXgf4U7AU6SLseKdanY195cwvU6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/core": "^10.2.0",
+ "@inquirer/figures": "^1.0.13",
+ "@inquirer/type": "^3.0.8",
+ "ansi-escapes": "^4.3.2",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/type": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.8.tgz",
+ "integrity": "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@isaacs/balanced-match": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz",
+ "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@isaacs/brace-expansion": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz",
+ "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@isaacs/balanced-match": "^4.0.1"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@istanbuljs/schema": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+ "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.30",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
+ "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@listr2/prompt-adapter-inquirer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.1.tgz",
+ "integrity": "sha512-3XFmGwm3u6ioREG+ynAQB7FoxfajgQnMhIu8wC5eo/Lsih4aKDg0VuIMGaOsYn7hJSJagSeaD4K8yfpkEoDEmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/type": "^3.0.7"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "@inquirer/prompts": ">= 3 < 8",
+ "listr2": "9.0.1"
+ }
+ },
+ "node_modules/@lmdb/lmdb-darwin-arm64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.4.2.tgz",
+ "integrity": "sha512-NK80WwDoODyPaSazKbzd3NEJ3ygePrkERilZshxBViBARNz21rmediktGHExoj9n5t9+ChlgLlxecdFKLCuCKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-darwin-x64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.4.2.tgz",
+ "integrity": "sha512-zevaowQNmrp3U7Fz1s9pls5aIgpKRsKb3dZWDINtLiozh3jZI9fBrI19lYYBxqdyiIyNdlyiidPnwPShj4aK+w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-linux-arm": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.4.2.tgz",
+ "integrity": "sha512-OmHCULY17rkx/RoCoXlzU7LyR8xqrksgdYWwtYa14l/sseezZ8seKWXcogHcjulBddER5NnEFV4L/Jtr2nyxeg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-linux-arm64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.4.2.tgz",
+ "integrity": "sha512-ZBEfbNZdkneebvZs98Lq30jMY8V9IJzckVeigGivV7nTHJc+89Ctomp1kAIWKlwIG0ovCDrFI448GzFPORANYg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-linux-x64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.4.2.tgz",
+ "integrity": "sha512-vL9nM17C77lohPYE4YaAQvfZCSVJSryE4fXdi8M7uWPBnU+9DJabgKVAeyDb84ZM2vcFseoBE4/AagVtJeRE7g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-win32-arm64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.4.2.tgz",
+ "integrity": "sha512-SXWjdBfNDze4ZPeLtYIzsIeDJDJ/SdsA0pEXcUBayUIMO0FQBHfVZZyHXQjjHr4cvOAzANBgIiqaXRwfMhzmLw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@lmdb/lmdb-win32-x64": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.4.2.tgz",
+ "integrity": "sha512-IY+r3bxKW6Q6sIPiMC0L533DEfRJSXibjSI3Ft/w9Q8KQBNqEIvUFXt+09wV8S5BRk0a8uSF19YWxuRwEfI90g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@modelcontextprotocol/sdk": {
+ "version": "1.17.3",
+ "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz",
+ "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.6",
+ "content-type": "^1.0.5",
+ "cors": "^2.8.5",
+ "cross-spawn": "^7.0.5",
+ "eventsource": "^3.0.2",
+ "eventsource-parser": "^3.0.0",
+ "express": "^5.0.1",
+ "express-rate-limit": "^7.5.0",
+ "pkce-challenge": "^5.0.0",
+ "raw-body": "^3.0.0",
+ "zod": "^3.23.8",
+ "zod-to-json-schema": "^3.24.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "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/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz",
+ "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz",
+ "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz",
+ "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz",
+ "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz",
+ "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz",
+ "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@napi-rs/nice": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz",
+ "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "optionalDependencies": {
+ "@napi-rs/nice-android-arm-eabi": "1.1.1",
+ "@napi-rs/nice-android-arm64": "1.1.1",
+ "@napi-rs/nice-darwin-arm64": "1.1.1",
+ "@napi-rs/nice-darwin-x64": "1.1.1",
+ "@napi-rs/nice-freebsd-x64": "1.1.1",
+ "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1",
+ "@napi-rs/nice-linux-arm64-gnu": "1.1.1",
+ "@napi-rs/nice-linux-arm64-musl": "1.1.1",
+ "@napi-rs/nice-linux-ppc64-gnu": "1.1.1",
+ "@napi-rs/nice-linux-riscv64-gnu": "1.1.1",
+ "@napi-rs/nice-linux-s390x-gnu": "1.1.1",
+ "@napi-rs/nice-linux-x64-gnu": "1.1.1",
+ "@napi-rs/nice-linux-x64-musl": "1.1.1",
+ "@napi-rs/nice-openharmony-arm64": "1.1.1",
+ "@napi-rs/nice-win32-arm64-msvc": "1.1.1",
+ "@napi-rs/nice-win32-ia32-msvc": "1.1.1",
+ "@napi-rs/nice-win32-x64-msvc": "1.1.1"
+ }
+ },
+ "node_modules/@napi-rs/nice-android-arm-eabi": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz",
+ "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-android-arm64": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz",
+ "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-darwin-arm64": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz",
+ "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-darwin-x64": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz",
+ "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-freebsd-x64": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz",
+ "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-arm-gnueabihf": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz",
+ "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-arm64-gnu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz",
+ "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-arm64-musl": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz",
+ "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-ppc64-gnu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz",
+ "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-riscv64-gnu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz",
+ "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-s390x-gnu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz",
+ "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-x64-gnu": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz",
+ "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-linux-x64-musl": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz",
+ "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-openharmony-arm64": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz",
+ "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-win32-arm64-msvc": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz",
+ "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-win32-ia32-msvc": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz",
+ "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/nice-win32-x64-msvc": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz",
+ "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.3.tgz",
+ "integrity": "sha512-rZxtMsLwjdXkMUGC3WwsPwLNVqVqnTJT6MNIB6e+5fhMcSCPP0AOsNWuMQ5mdCq6HNjs/ZeWAEchpqeprqBD2Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.5",
+ "@emnapi/runtime": "^1.4.5",
+ "@tybys/wasm-util": "^0.10.0"
+ }
+ },
+ "node_modules/@npmcli/agent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz",
+ "integrity": "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "http-proxy-agent": "^7.0.0",
+ "https-proxy-agent": "^7.0.1",
+ "lru-cache": "^10.0.1",
+ "socks-proxy-agent": "^8.0.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/agent/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@npmcli/fs": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz",
+ "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/git": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-6.0.3.tgz",
+ "integrity": "sha512-GUYESQlxZRAdhs3UhbB6pVRNUELQOHXwK9ruDkwmCv2aZ5y0SApQzUJCg02p3A7Ue2J5hxvlk1YI53c00NmRyQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/promise-spawn": "^8.0.0",
+ "ini": "^5.0.0",
+ "lru-cache": "^10.0.1",
+ "npm-pick-manifest": "^10.0.0",
+ "proc-log": "^5.0.0",
+ "promise-retry": "^2.0.1",
+ "semver": "^7.3.5",
+ "which": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/git/node_modules/isexe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@npmcli/git/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@npmcli/git/node_modules/which": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/installed-package-contents": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz",
+ "integrity": "sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-bundled": "^4.0.0",
+ "npm-normalize-package-bin": "^4.0.0"
+ },
+ "bin": {
+ "installed-package-contents": "bin/index.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/node-gyp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz",
+ "integrity": "sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/package-json": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.2.0.tgz",
+ "integrity": "sha512-rCNLSB/JzNvot0SEyXqWZ7tX2B5dD2a1br2Dp0vSYVo5jh8Z0EZ7lS9TsZ1UtziddB1UfNUaMCc538/HztnJGA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^6.0.0",
+ "glob": "^10.2.2",
+ "hosted-git-info": "^8.0.0",
+ "json-parse-even-better-errors": "^4.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.5.3",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/package-json/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@npmcli/package-json/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@npmcli/package-json/node_modules/hosted-git-info": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz",
+ "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/package-json/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/@npmcli/package-json/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@npmcli/promise-spawn": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz",
+ "integrity": "sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "which": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/promise-spawn/node_modules/isexe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@npmcli/promise-spawn/node_modules/which": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/redact": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@npmcli/redact/-/redact-3.2.2.tgz",
+ "integrity": "sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/run-script": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-9.1.0.tgz",
+ "integrity": "sha512-aoNSbxtkePXUlbZB+anS1LqsJdctG5n3UVhfU47+CDdwMi6uNTBMF9gPcQRnqghQd2FGzcwwIFBruFMxjhBewg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/node-gyp": "^4.0.0",
+ "@npmcli/package-json": "^6.0.0",
+ "@npmcli/promise-spawn": "^8.0.0",
+ "node-gyp": "^11.0.0",
+ "proc-log": "^5.0.0",
+ "which": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@npmcli/run-script/node_modules/isexe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@npmcli/run-script/node_modules/which": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@oxc-project/runtime": {
+ "version": "0.81.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.81.0.tgz",
+ "integrity": "sha512-zm/LDVOq9FEmHiuM8zO4DWirv0VP2Tv2VsgaiHby9nvpq+FVrcqNYgv+TysLKOITQXWZj/roluTxFvpkHP0Iuw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@oxc-project/types": {
+ "version": "0.81.0",
+ "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.81.0.tgz",
+ "integrity": "sha512-CnOqkybZK8z6Gx7Wb1qF7AEnSzbol1WwcIzxYOr8e91LytGOjo0wCpgoYWZo8sdbpqX+X+TJayIzo4Pv0R/KjA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Boshen"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz",
+ "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^1.0.3",
+ "is-glob": "^4.0.3",
+ "micromatch": "^4.0.5",
+ "node-addon-api": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.1",
+ "@parcel/watcher-darwin-arm64": "2.5.1",
+ "@parcel/watcher-darwin-x64": "2.5.1",
+ "@parcel/watcher-freebsd-x64": "2.5.1",
+ "@parcel/watcher-linux-arm-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm-musl": "2.5.1",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.1",
+ "@parcel/watcher-linux-arm64-musl": "2.5.1",
+ "@parcel/watcher-linux-x64-glibc": "2.5.1",
+ "@parcel/watcher-linux-x64-musl": "2.5.1",
+ "@parcel/watcher-win32-arm64": "2.5.1",
+ "@parcel/watcher-win32-ia32": "2.5.1",
+ "@parcel/watcher-win32-x64": "2.5.1"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz",
+ "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz",
+ "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz",
+ "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz",
+ "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz",
+ "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz",
+ "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz",
+ "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz",
+ "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz",
+ "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
+ "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz",
+ "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz",
+ "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz",
+ "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher/node_modules/detect-libc": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
+ "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "bin": {
+ "detect-libc": "bin/detect-libc.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/@parcel/watcher/node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@progress/jszip-esm": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@progress/jszip-esm/-/jszip-esm-1.0.4.tgz",
+ "integrity": "sha512-A5i26JcTosFKeHCrklarNsByW3RUJd8osRq69eskZgIaq05weTCXdpztlFMwrHpgOGods1D0WFoSQcMNE0eI8Q==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/pako-esm": "^1.0.1"
+ }
+ },
+ "node_modules/@progress/kendo-angular-buttons": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-buttons/-/kendo-angular-buttons-20.0.3.tgz",
+ "integrity": "sha512-X0I5fyl0RUAhm8cbATeGFNC6XhnjsPyIpGXWn4s5po93T9BxGeFMpom/jKsIYNQPrRVYbYSo9TheC5/xGh4TuQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-webspeech-common": "1.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-charts": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-charts/-/kendo-angular-charts-20.0.3.tgz",
+ "integrity": "sha512-8VKilTQufdAuel5yiOdcO5+bf53O3JP/DclPKbIhu18ydjudXlV/zT/MW5n9RwCbYbegsCMPU9mVyvUJI4uFOw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-charts": "2.8.0",
+ "@progress/kendo-svg-icons": "^4.0.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-navigation": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-common": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-common/-/kendo-angular-common-20.0.3.tgz",
+ "integrity": "sha512-vhr4AJa0HvoecKcG9EF35Wf4CuzBFm/yd+xny/GO5LioTQreeEVqpkFLLwchToU8+Tq1NWhIKYTdwUefl3eB6A==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-draggable": "^3.0.2",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-conversational-ui": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-conversational-ui/-/kendo-angular-conversational-ui-20.0.3.tgz",
+ "integrity": "sha512-yeCEnG73tNajkdmeFju4uPEcLGZsxIhhbZkgzoaNjV+DHSfTc6Iep1glJGZ5d2wurkADHJJ4JuuW9DPFUEsnnQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-inputs": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-layout": "20.0.3",
+ "@progress/kendo-angular-menu": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-toolbar": "20.0.3",
+ "@progress/kendo-angular-upload": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-dateinputs": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-dateinputs/-/kendo-angular-dateinputs-20.0.3.tgz",
+ "integrity": "sha512-wZbsCiyDZA4cYUKIerOXnDfnr3Hqpy4SkE04gf5enno4nSUetrPBAhkKBCDUJBD0QfIft7TbFQC/DntWuwVjYA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-date-math": "^1.1.0",
+ "@progress/kendo-dateinputs-common": "^0.4.6",
+ "node-html-parser": "^7.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-navigation": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-utils": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-dialog": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-dialog/-/kendo-angular-dialog-20.0.3.tgz",
+ "integrity": "sha512-HuMLwQMtIrTgHMEhBp3/Rpl8EtzHbbqFpG5TcNkwomkAptRyQwHqlzo6v/S94Zc5WtVQdwjmS8wQLq68XFH4GA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-popup-common": "^1.7.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-dropdowns": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-dropdowns/-/kendo-angular-dropdowns-20.0.3.tgz",
+ "integrity": "sha512-RBSOTkkTsYIT9fjB5GiQVhuBr7kj4Guqryd7A9xTvkka+VdR1Yh3+vRjPkB/VvPVllzNK1qpBDCCzaNQ9HmgUw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "node-html-parser": "^7.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-navigation": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-treeview": "20.0.3",
+ "@progress/kendo-angular-utils": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-editor": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-editor/-/kendo-angular-editor-20.0.3.tgz",
+ "integrity": "sha512-bSI9GfHlYLghxjAXHTmVVCiZx61a6dCi4NFg97sE/AqUHaSE2Aa5tBVdCwb6MDezDJAb6RHyawI6MZxbI14L3w==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-editor-common": "1.12.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-dialog": "20.0.3",
+ "@progress/kendo-angular-dropdowns": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-inputs": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-label": "20.0.3",
+ "@progress/kendo-angular-layout": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-toolbar": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-excel-export": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-excel-export/-/kendo-angular-excel-export-20.0.3.tgz",
+ "integrity": "sha512-AzIgnkvun8Yr/AM859d+HSP9lZAfSIRc31GbPiU+mp4lVdUTLcCfb0L731tRcOKbC6CCAnxDMYSZLOxbb2CR6Q==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-file-saver": "^1.0.0",
+ "@progress/kendo-intl": "^3.0.0",
+ "@progress/kendo-ooxml": "^1.9.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-gauges": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-gauges/-/kendo-angular-gauges-20.0.3.tgz",
+ "integrity": "sha512-HwIBcZxf1UmDwmP3HXnLA+AIJraDr4qDkNDFWN1QnHIcL+pD38QmCdvXX3Sl374aK/QYCdk2irENjpBWYV9Gpg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-charts": "2.8.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-grid": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-grid/-/kendo-angular-grid-20.0.3.tgz",
+ "integrity": "sha512-l1zuhn7mPFXE1qcgozM4NyP4qzZTfok0kh1vM2+z+qUzS9Xj75VGemsEhKBMxguibdxIcqeGiqnttLXrR8dUcg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-file-saver": "^1.0.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-conversational-ui": "20.0.3",
+ "@progress/kendo-angular-dateinputs": "20.0.3",
+ "@progress/kendo-angular-dropdowns": "20.0.3",
+ "@progress/kendo-angular-excel-export": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-inputs": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-label": "20.0.3",
+ "@progress/kendo-angular-layout": "20.0.3",
+ "@progress/kendo-angular-navigation": "20.0.3",
+ "@progress/kendo-angular-pager": "20.0.3",
+ "@progress/kendo-angular-pdf-export": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-toolbar": "20.0.3",
+ "@progress/kendo-angular-utils": "20.0.3",
+ "@progress/kendo-data-query": "^1.0.0",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-icons": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-icons/-/kendo-angular-icons-20.0.3.tgz",
+ "integrity": "sha512-2QI6siZeCfC3ABWbm8It8fgm5RitqF4UEBooPFU0aBQPUdm/LtcQq653nozBRCAsUZUgRVOu9++D74LFp1JERw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "@progress/kendo-svg-icons": "^4.0.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-indicators": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-indicators/-/kendo-angular-indicators-20.0.3.tgz",
+ "integrity": "sha512-mZDIZWf93yp72aoKWHCPlgBmilVvjDMV+s6SPW5Rkc60xEpIoCbJlkVKw9/de6VrOYXT7ecHPbRyYR5sl4i7zQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-inputs": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-inputs/-/kendo-angular-inputs-20.0.3.tgz",
+ "integrity": "sha512-gIVbgltclEV2l2bqXTX5num6E9Jfe9vrhUKT6lbVCcYdvZKw7NPq66OFmC9utVNwy7RGN5MUgbqC7T66IQkibA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-draggable": "^3.0.0",
+ "@progress/kendo-inputs-common": "^3.1.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-dialog": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-navigation": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-angular-utils": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-intl": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-intl/-/kendo-angular-intl-20.0.3.tgz",
+ "integrity": "sha512-4f+kD+qTyyE6QGcXTlJoQHY1L9rMbDx70fzZfHBRZywyoURJ7Ha9Jpkr9wEEWCLworrh0Uph6TFOx02ly4li6g==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-intl": "^3.1.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-l10n": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-l10n/-/kendo-angular-l10n-20.0.3.tgz",
+ "integrity": "sha512-wIdAbOYUlEP9aN9JImizy0ZJVeayZb3j1DiRf7+LRDLonVpoqKpS3dMVlwOyl5ndAoawg0CYswHx/SCPWViPKQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-label": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-label/-/kendo-angular-label-20.0.3.tgz",
+ "integrity": "sha512-a0Ib0lTzaVjp6a1uVSrRAme+ba4IUTPY17uO1HnYnvQkMh652AsWy+5JgU5yakq6w/GSQVM2dnwLPxXez0xE5A==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-layout": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-layout/-/kendo-angular-layout-20.0.3.tgz",
+ "integrity": "sha512-vR/YfMC2QDA2+G0MKuVCrS59rWXpccSq9+uyDkwtZSpIIx6AlAfvLDM9E/QisZsFYSC6ysLzszksXycMLRLWmg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-draggable": "^3.0.2",
+ "node-html-parser": "^7.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-intl": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-progressbar": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-listview": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-listview/-/kendo-angular-listview-20.0.3.tgz",
+ "integrity": "sha512-q50iaSRtno82DcLdTi5AV5+Yk2jMXvz7/EUotHyjVyMa0k/3Q+9PzLMgsjlHzgd6KTFid+eJrgq8Q/wF3tLnTQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-pager": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-map": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-map/-/kendo-angular-map-20.0.3.tgz",
+ "integrity": "sha512-p5QpblvDUAZt4HdxJGxmuhc1gtMdq/KaIdoV1/lIJ06J3GQCbGcLAwq0rUHVtWroxQR2dhV6sfESR/F5Pg2uEw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-charts": "2.8.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.4.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-menu": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-menu/-/kendo-angular-menu-20.0.3.tgz",
+ "integrity": "sha512-6retMcGkONcUndpXT3whCJKP7OaUnf2Z2PZrWuZ94E8Z3FbPxaorbUbUmmLtCupP+1PQ8Q9RPyLdI89kxkDn9w==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-navigation": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-navigation/-/kendo-angular-navigation-20.0.3.tgz",
+ "integrity": "sha512-Tcxub3lG5JQn8r7ub8b7VP6fVGhp12fueZdIHVYD5dTfuFIJNPPsRiuoTx+gFsSA5zX4KWHfDRYAMk8yf4QBEg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-pager": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-pager/-/kendo-angular-pager-20.0.3.tgz",
+ "integrity": "sha512-lUMZ3GO30CveWntR3woKPPK9wKN+m2kvXEd5TnqsCx5RFYhzyLqkCRUpkkUeTPNGH31ykJay4mLLWBCsnINTyA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-dropdowns": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-inputs": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-pdf-export": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-pdf-export/-/kendo-angular-pdf-export-20.0.3.tgz",
+ "integrity": "sha512-AcmNUwHY6n8bYL+rXvzKQSS95om99b/NtnsTaTbmZ7StSwSVelk+DCK88a7odZ9NWnForyMgxQNwlw5rceD1Aw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-file-saver": "^1.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-drawing": "^1.21.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-popup": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-popup/-/kendo-angular-popup-20.0.3.tgz",
+ "integrity": "sha512-2Uql09Gg1ZFsHpmMR/iV5U5EHvpeoykJ3ixZG/bCcy58i11ku/OjjlKnLkZPGGYZD/H6UFKBy+T2UOYqwDEHCw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-popup-common": "^1.7.0",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-progressbar": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-progressbar/-/kendo-angular-progressbar-20.0.3.tgz",
+ "integrity": "sha512-JPhgOSEOZFGUA5s7SCnTQZHodt3ee8IzlShzVcP6R/HhHRCrMq3G6cEXoLvj1GB5t5Ilc3eWlm9w4H7xEOuYVg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-schematics": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-schematics/-/kendo-angular-schematics-20.0.3.tgz",
+ "integrity": "sha512-2SVJOFfwrv1yy36HJgoU8veQlp268OzRh3D9E+hLzj7uYllxbDLIWEy5cGMt3PMUuikkqgARAVeTSbJP0wYKcw==",
+ "license": "SEE LICENSE in LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@types/semver": "^7.5.0",
+ "semver": "^7.5.3"
+ },
+ "peerDependencies": {
+ "@angular-devkit/core": "16 - 20",
+ "@angular-devkit/schematics": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@schematics/angular": "16 - 20",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-scrollview": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-scrollview/-/kendo-angular-scrollview-20.0.3.tgz",
+ "integrity": "sha512-Crd0K53wqetPMaQyt2I2FALK5/lKLRWQ72EwXG4lcokrMzLrqN663Pe2H2IgatCNt3jGQ9P91Xw0J0B1Hu6sXw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-toolbar": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-toolbar/-/kendo-angular-toolbar-20.0.3.tgz",
+ "integrity": "sha512-CM1Pd8I13eBDbXpRKVOj9C4YbwsiWCa99UCkdbaDVvfTJ7YcDkEvWh9xI5n5FsIYHp3p0DakyNUWeX4iMsAIUA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "node-html-parser": "^7.0.1",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-indicators": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-tooltip": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-tooltip/-/kendo-angular-tooltip-20.0.3.tgz",
+ "integrity": "sha512-rEf9LADFa8IXu7zihhV++BtWoi7PgKyh+CKqrUMyQUz/yqSJ2xz7nnED6W6u7fO1GqOyh9sI68K+/tkeNyUkZA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-popup": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-treeview": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-treeview/-/kendo-angular-treeview-20.0.3.tgz",
+ "integrity": "sha512-ge/uuWXXB5BEt3N5CJ2L7v0FQtvifL+Ftigq1Yi2GBqEKSWcPsEx/lpoDI4drbr/QyUz9PgYUzE4TZDMB2W0Bw==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/kendo-draggable": "^3.0.2",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-inputs": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-upload": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-upload/-/kendo-angular-upload-20.0.3.tgz",
+ "integrity": "sha512-IXFRsEQeARFnkgyiaNP686vOFSNvYujcmqfQoXDKevgV3XCFfNLQZRAD34rT04m1eFEnbeo6ZbpSYV1VQfmcrA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/forms": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-buttons": "20.0.3",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-angular-icons": "20.0.3",
+ "@progress/kendo-angular-l10n": "20.0.3",
+ "@progress/kendo-angular-progressbar": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-angular-utils": {
+ "version": "20.0.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-angular-utils/-/kendo-angular-utils-20.0.3.tgz",
+ "integrity": "sha512-MIkQLxPToryj33ed9RX/DKMnhG32bpCC5h8rD6WlmCpFBCwxFKrtovluQrc1lK6fMIT9yXzwmSHi3hILtc6O+w==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-angular-schematics": "20.0.3",
+ "@progress/kendo-draggable-common": "^0.2.3",
+ "tslib": "^2.3.1"
+ },
+ "peerDependencies": {
+ "@angular/animations": "16 - 20",
+ "@angular/common": "16 - 20",
+ "@angular/core": "16 - 20",
+ "@angular/platform-browser": "16 - 20",
+ "@progress/kendo-angular-common": "20.0.3",
+ "@progress/kendo-licensing": "^1.7.0",
+ "rxjs": "^6.5.3 || ^7.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-charts": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-charts/-/kendo-charts-2.8.0.tgz",
+ "integrity": "sha512-KHP6RgsCSFDTO1ZvdWgqK/9t0z/KEthhrqAmPC/EC5dqezGqfJJ5H1Y/Cw1e9HeQ4qgtZXz/q8R84cCJg/hTiQ==",
+ "license": "SEE LICENSE IN license.txt",
+ "peerDependencies": {
+ "@progress/kendo-drawing": "^1.21.0"
+ }
+ },
+ "node_modules/@progress/kendo-common": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-common/-/kendo-common-1.0.2.tgz",
+ "integrity": "sha512-PHxnquetSmtmXiF4dmlQiypzXaFLUEPK3VAOHxmnRDrLxaPrcZfaW9FOOiyur8hv4QmXlohISMwMElZS8Xi1Ag==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "tslib": "^1.7.0"
+ }
+ },
+ "node_modules/@progress/kendo-common/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "license": "0BSD"
+ },
+ "node_modules/@progress/kendo-data-query": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-data-query/-/kendo-data-query-1.7.1.tgz",
+ "integrity": "sha512-1ax6mNx1XVr5A8d9VhzuZprAq1il7oES+XwIGnLikCmkKnFk+jcBmGVksw4MKB+kcdGzQPd4RV4iO6G0kaknEA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@progress/kendo-date-math": {
+ "version": "1.5.15",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-date-math/-/kendo-date-math-1.5.15.tgz",
+ "integrity": "sha512-muQ5LtWARWArq0TpexjtyGmpFbF93Vwc9C+bPVOuidipBcR3j0SuLfO4b7pAmE4AWbEXScOEgkt/6Uw7iL/cZA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "tslib": "^2.4.1"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=10.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-dateinputs-common": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-dateinputs-common/-/kendo-dateinputs-common-0.4.6.tgz",
+ "integrity": "sha512-ZEU6wCTXxmmS3qDUyKuk+5/uNUtZJ+ONJ/F8UALb5Dqk+AUAJSh/dKX0HvZLK9PcIV/s/8aIFlIAXWoqBFaG1w==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-date-math": "^1.5.9",
+ "tslib": "^2.4.1"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=10.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-draggable": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-draggable/-/kendo-draggable-3.1.0.tgz",
+ "integrity": "sha512-S5AHF9uiy44um+06ABJcjZn/wpO3ZwLahd2BhiTd7NeBVPt5lkj2bjdmkd88GEIIBKmT7FOK308WUt5/MmKVTQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-draggable-common": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-draggable-common/-/kendo-draggable-common-0.2.3.tgz",
+ "integrity": "sha512-e1FraFsT7zwevswzZlQYL//K+fzmRUvkr/4emp51dzkARLDtGd95BtPNSoXYRG5xYHeueKBS75hzVwQI6Dm3Dg==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "tslib": "^1.7.0"
+ }
+ },
+ "node_modules/@progress/kendo-draggable-common/node_modules/tslib": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz",
+ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==",
+ "license": "0BSD"
+ },
+ "node_modules/@progress/kendo-drawing": {
+ "version": "1.22.2",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-drawing/-/kendo-drawing-1.22.2.tgz",
+ "integrity": "sha512-/X5sSgB4XWTtPfOpew0PKwkRJ9hABcmKEWLMVI/zIiiZDxm65txDWUmllsIOhOL4ADr5M89qJfSFbNRq8MRIHA==",
+ "license": "See license in LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-common": "^1.0.1",
+ "@progress/pako-esm": "^1.0.1"
+ }
+ },
+ "node_modules/@progress/kendo-editor-common": {
+ "version": "1.12.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-editor-common/-/kendo-editor-common-1.12.3.tgz",
+ "integrity": "sha512-vuB0ZE60uBqBTPOEP4YHkr1yJIVB4/HEq5YzrpH1CRuhn1Oexq1r0hItw+VgAEw3cfwdXQnCi20ZtnTvH7GepQ==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/kendo-common": "^1.0.2",
+ "prosemirror-commands": "1.7.1",
+ "prosemirror-dropcursor": "1.8.2",
+ "prosemirror-gapcursor": "1.3.2",
+ "prosemirror-history": "1.4.1",
+ "prosemirror-inputrules": "1.5.0",
+ "prosemirror-keymap": "1.2.3",
+ "prosemirror-model": "1.25.1",
+ "prosemirror-schema-list": "1.5.1",
+ "prosemirror-state": "1.4.3",
+ "prosemirror-tables": "1.7.1",
+ "prosemirror-transform": "1.10.4",
+ "prosemirror-view": "1.39.3",
+ "tslib": "^2.8.1"
+ }
+ },
+ "node_modules/@progress/kendo-file-saver": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-file-saver/-/kendo-file-saver-1.1.2.tgz",
+ "integrity": "sha512-hWpJ67L8b2+GIhsIWR09NgGaEh87jvcHv7kScC671cbVWJycXTGqdy3ZoI0pzIaH8K0IgP2TNkF1ay4HGxe+pg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-inputs-common": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-inputs-common/-/kendo-inputs-common-3.1.2.tgz",
+ "integrity": "sha512-iCYz0yuDeR/YS+mW5tY7UkWEsf5EjdTKLpx8O5VJAFFcd2dxLCK/HAihSHS+KpWqIZIw4EIWeR6lzGQf0nN0nA==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "tslib": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "@progress/kendo-drawing": "^1.21.2"
+ }
+ },
+ "node_modules/@progress/kendo-intl": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-intl/-/kendo-intl-3.1.2.tgz",
+ "integrity": "sha512-rOtMppQSrScwryMfeQSOdsnRi9Oj1l08HFoEC2ticZ0T2N0/JN9CHt+fuToRx5onXK7QkcbbuNM0D09o8TeeMw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-licensing": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-licensing/-/kendo-licensing-1.7.1.tgz",
+ "integrity": "sha512-7uYrYjd++/4SVYrjoywT88EbG4pnpkAnYWRpshToVwsSDoprwurtW1Jo6Pzz0kwCIbQYBinY5PDroSdrE0tgVQ==",
+ "hasInstallScript": true,
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "glob": "^10.4.5",
+ "jsonwebtoken": "^9.0.2"
+ },
+ "bin": {
+ "kendo-ui-license": "bin/kendo-ui-license.js"
+ }
+ },
+ "node_modules/@progress/kendo-licensing/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-licensing/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@progress/kendo-licensing/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@progress/kendo-ooxml": {
+ "version": "1.9.3",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-ooxml/-/kendo-ooxml-1.9.3.tgz",
+ "integrity": "sha512-RUrn3k6IiWteD79vaRTkbRWhYKiHr0QzBswmuEnNBj0UySVjJ5ScPOytpRYcwkKE4sUq1JnclmoGaWpjX/o6og==",
+ "license": "SEE LICENSE IN LICENSE.md",
+ "dependencies": {
+ "@progress/jszip-esm": "^1.0.4",
+ "@progress/pako-esm": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=10.0.0"
+ }
+ },
+ "node_modules/@progress/kendo-popup-common": {
+ "version": "1.9.5",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-popup-common/-/kendo-popup-common-1.9.5.tgz",
+ "integrity": "sha512-HpUy0PLbS/bhhs/FxxFGaJxxbskQbAVMPWE5i6qxQ5G1krQHWApJ9B+43Rx5IQiWtoenjCGtYv0QSZlRQxDqsg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-svg-icons": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-svg-icons/-/kendo-svg-icons-4.5.0.tgz",
+ "integrity": "sha512-KO7KZNrm1Fi8jR4HLvaPmwzzsvswAZUlm6jQ0jHz9ck/iuREo8YWIUaVQxnzcmtCT3nRyrYMofLa13eR4PEhQg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-theme-core": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-theme-core/-/kendo-theme-core-12.1.0.tgz",
+ "integrity": "sha512-31Ibhb11hxJVXY457q8elrxeGFDO4g3ANKwB2XTzNksufwY/ky7DRVGQkUs6nBmVcI2us8B997l7l12kEhMhzg==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@progress/kendo-theme-default": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-theme-default/-/kendo-theme-default-12.1.0.tgz",
+ "integrity": "sha512-KaUi+cC9IpxKE9Nd8iunDUioxoHMj5pl69LRiOnr2nc+5kxvaCC3pEY/tcEU4o4amPa9QEFaQdzAFg5jeUaT+Q==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@progress/kendo-svg-icons": "^4.5.0",
+ "@progress/kendo-theme-core": "12.1.0",
+ "@progress/kendo-theme-utils": "12.1.0"
+ }
+ },
+ "node_modules/@progress/kendo-theme-utils": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-theme-utils/-/kendo-theme-utils-12.1.0.tgz",
+ "integrity": "sha512-3tZY/x6Ly/lcfiVNEw36hyhR6FnF9IM3MWUOAPa40+zNHYIDLvCcuNTN5yBovUoCTlrVg9FszxNstmun3qAObw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@progress/kendo-theme-core": "12.1.0"
+ }
+ },
+ "node_modules/@progress/kendo-webspeech-common": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@progress/kendo-webspeech-common/-/kendo-webspeech-common-1.0.1.tgz",
+ "integrity": "sha512-c5IHS5vakAtxbaVfd26rM1dZnt9JfUOqWFq7gK8mU3HaZ9rqoH+DEIn72bbiRs+Ju5e81d7H1Q/nMx8u09pwRg==",
+ "license": "SEE LICENSE IN LICENSE.md"
+ },
+ "node_modules/@progress/pako-esm": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@progress/pako-esm/-/pako-esm-1.0.1.tgz",
+ "integrity": "sha512-O4A3b1EuE9Xe1pC3Xz9Tcn1M/CYrL71f4y/5TXeytOVTkmkzBgYW97fYP2f+54H0e0erWRaqV/kUUB/a8Uxfbw==",
+ "license": "SEE LICENSE IN LICENSE.md"
+ },
+ "node_modules/@rolldown/binding-android-arm64": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.32.tgz",
+ "integrity": "sha512-Gs+313LfR4Ka3hvifdag9r44WrdKQaohya7ZXUXzARF7yx0atzFlVZjsvxtKAw1Vmtr4hB/RjUD1jf73SW7zDw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rolldown/binding-darwin-arm64": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.32.tgz",
+ "integrity": "sha512-W8oMqzGcI7wKPXUtS3WJNXzbghHfNiuM1UBAGpVb+XlUCgYRQJd2PRGP7D3WGql3rR3QEhUvSyAuCBAftPQw6Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rolldown/binding-darwin-x64": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.32.tgz",
+ "integrity": "sha512-pM4c4sKUk37noJrnnDkJknLhCsfZu7aWyfe67bD0GQHfzAPjV16wPeD9CmQg4/0vv+5IfHYaa4VE536xbA+W0Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rolldown/binding-freebsd-x64": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.32.tgz",
+ "integrity": "sha512-M8SUgFlYb5kJJWcFC8gUMRiX4WLFxPKMed3SJ2YrxontgIrEcpizPU8nLNVsRYEStoSfKHKExpQw3OP6fm+5bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rolldown/binding-linux-arm-gnueabihf": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.32.tgz",
+ "integrity": "sha512-FuQpbNC/hE//bvv29PFnk0AtpJzdPdYl5CMhlWPovd9g3Kc3lw9TrEPIbL7gRPUdhKAiq6rVaaGvOnXxsa0eww==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rolldown/binding-linux-arm64-gnu": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.32.tgz",
+ "integrity": "sha512-hRZygRlaGCjcNTNY9GV7dDI18sG1dK3cc7ujHq72LoDad23zFDUGMQjiSxHWK+/r92iMV+j2MiHbvzayxqynsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rolldown/binding-linux-arm64-musl": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.32.tgz",
+ "integrity": "sha512-HzgT6h+CXLs+GKAU0Wvkt3rvcv0CmDBsDjlPhh4GHysOKbG9NjpKYX2zvjx671E9pGbTvcPpwy7gGsy7xpu+8g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rolldown/binding-linux-x64-gnu": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.32.tgz",
+ "integrity": "sha512-Ab/wbf6gdzphDbsg51UaxsC93foQ7wxhtg0SVCXd25BrV4MAJ1HoDtKN/f4h0maFmJobkqYub2DlmoasUzkvBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rolldown/binding-linux-x64-musl": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.32.tgz",
+ "integrity": "sha512-VoxqGEfh5A1Yx+zBp/FR5QwAbtzbuvky2SVc+ii4g1gLD4zww6mt/hPi5zG+b88zYPFBKHpxMtsz9cWqXU5V5Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rolldown/binding-openharmony-arm64": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.32.tgz",
+ "integrity": "sha512-qZ1ViyOUDGbiZrSAJ/FIAhYUElDfVxxFW6DLT/w4KeoZN3HsF4jmRP95mXtl51/oGrqzU9l9Q2f7/P4O/o2ZZA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rolldown/binding-wasm32-wasi": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.32.tgz",
+ "integrity": "sha512-hEkG3wD+f3wytV0lqwb/uCrXc4r4Ny/DWJFJPfQR3VeMWplhWGgSHNwZc2Q7k86Yi36f9NNzzWmrIuvHI9lCVw==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@napi-rs/wasm-runtime": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@rolldown/binding-win32-arm64-msvc": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.32.tgz",
+ "integrity": "sha512-k3MvDf8SiA7uP2ikP0unNouJ2YCrnwi7xcVW+RDgMp5YXVr3Xu6svmT3HGn0tkCKUuPmf+uy8I5uiHt5qWQbew==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rolldown/binding-win32-ia32-msvc": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.32.tgz",
+ "integrity": "sha512-wAi/FxGh7arDOUG45UmnXE1sZUa0hY4cXAO2qWAjFa3f7bTgz/BqwJ7XN5SUezvAJPNkME4fEpInfnBvM25a0w==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rolldown/binding-win32-x64-msvc": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.32.tgz",
+ "integrity": "sha512-Ej0i4PZk8ltblZtzVK8ouaGUacUtxRmTm5S9794mdyU/tYxXjAJNseOfxrnHpMWKjMDrOKbqkPqJ52T9NR4LQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.32.tgz",
+ "integrity": "sha512-QReCdvxiUZAPkvp1xpAg62IeNzykOFA6syH2CnClif4YmALN1XKpB39XneL80008UbtMShthSVDKmrx05N1q/g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.49.0.tgz",
+ "integrity": "sha512-rlKIeL854Ed0e09QGYFlmDNbka6I3EQFw7iZuugQjMb11KMpJCLPFL4ZPbMfaEhLADEL1yx0oujGkBQ7+qW3eA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.49.0.tgz",
+ "integrity": "sha512-cqPpZdKUSQYRtLLr6R4X3sD4jCBO1zUmeo3qrWBCqYIeH8Q3KRL4F3V7XJ2Rm8/RJOQBZuqzQGWPjjvFUcYa/w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.49.0.tgz",
+ "integrity": "sha512-99kMMSMQT7got6iYX3yyIiJfFndpojBmkHfTc1rIje8VbjhmqBXE+nb7ZZP3A5skLyujvT0eIUCUsxAe6NjWbw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.49.0.tgz",
+ "integrity": "sha512-y8cXoD3wdWUDpjOLMKLx6l+NFz3NlkWKcBCBfttUn+VGSfgsQ5o/yDUGtzE9HvsodkP0+16N0P4Ty1VuhtRUGg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.49.0.tgz",
+ "integrity": "sha512-3mY5Pr7qv4GS4ZvWoSP8zha8YoiqrU+e0ViPvB549jvliBbdNLrg2ywPGkgLC3cmvN8ya3za+Q2xVyT6z+vZqA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.49.0.tgz",
+ "integrity": "sha512-C9KzzOAQU5gU4kG8DTk+tjdKjpWhVWd5uVkinCwwFub2m7cDYLOdtXoMrExfeBmeRy9kBQMkiyJ+HULyF1yj9w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.49.0.tgz",
+ "integrity": "sha512-OVSQgEZDVLnTbMq5NBs6xkmz3AADByCWI4RdKSFNlDsYXdFtlxS59J+w+LippJe8KcmeSSM3ba+GlsM9+WwC1w==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.49.0.tgz",
+ "integrity": "sha512-ZnfSFA7fDUHNa4P3VwAcfaBLakCbYaxCk0jUnS3dTou9P95kwoOLAMlT3WmEJDBCSrOEFFV0Y1HXiwfLYJuLlA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.49.0.tgz",
+ "integrity": "sha512-Z81u+gfrobVK2iV7GqZCBfEB1y6+I61AH466lNK+xy1jfqFLiQ9Qv716WUM5fxFrYxwC7ziVdZRU9qvGHkYIJg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.49.0.tgz",
+ "integrity": "sha512-zoAwS0KCXSnTp9NH/h9aamBAIve0DXeYpll85shf9NJ0URjSTzzS+Z9evmolN+ICfD3v8skKUPyk2PO0uGdFqg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.49.0.tgz",
+ "integrity": "sha512-2QyUyQQ1ZtwZGiq0nvODL+vLJBtciItC3/5cYN8ncDQcv5avrt2MbKt1XU/vFAJlLta5KujqyHdYtdag4YEjYQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.49.0.tgz",
+ "integrity": "sha512-k9aEmOWt+mrMuD3skjVJSSxHckJp+SiFzFG+v8JLXbc/xi9hv2icSkR3U7uQzqy+/QbbYY7iNB9eDTwrELo14g==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.49.0.tgz",
+ "integrity": "sha512-rDKRFFIWJ/zJn6uk2IdYLc09Z7zkE5IFIOWqpuU0o6ZpHcdniAyWkwSUWE/Z25N/wNDmFHHMzin84qW7Wzkjsw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.49.0.tgz",
+ "integrity": "sha512-FkkhIY/hYFVnOzz1WeV3S9Bd1h0hda/gRqvZCMpHWDHdiIHn6pqsY3b5eSbvGccWHMQ1uUzgZTKS4oGpykf8Tw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.49.0.tgz",
+ "integrity": "sha512-gRf5c+A7QiOG3UwLyOOtyJMD31JJhMjBvpfhAitPAoqZFcOeK3Kc1Veg1z/trmt+2P6F/biT02fU19GGTS529A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.49.0.tgz",
+ "integrity": "sha512-BR7+blScdLW1h/2hB/2oXM+dhTmpW3rQt1DeSiCP9mc2NMMkqVgjIN3DDsNpKmezffGC9R8XKVOLmBkRUcK/sA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.49.0.tgz",
+ "integrity": "sha512-hDMOAe+6nX3V5ei1I7Au3wcr9h3ktKzDvF2ne5ovX8RZiAHEtX1A5SNNk4zt1Qt77CmnbqT+upb/umzoPMWiPg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.49.0.tgz",
+ "integrity": "sha512-wkNRzfiIGaElC9kXUT+HLx17z7D0jl+9tGYRKwd8r7cUqTL7GYAvgUY++U2hK6Ar7z5Z6IRRoWC8kQxpmM7TDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.49.0.tgz",
+ "integrity": "sha512-gq5aW/SyNpjp71AAzroH37DtINDcX1Qw2iv9Chyz49ZgdOP3NV8QCyKZUrGsYX9Yyggj5soFiRCgsL3HwD8TdA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.49.0.tgz",
+ "integrity": "sha512-gEtqFbzmZLFk2xKh7g0Rlo8xzho8KrEFEkzvHbfUGkrgXOpZ4XagQ6n+wIZFNh1nTb8UD16J4nFSFKXYgnbdBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@schematics/angular": {
+ "version": "20.2.0",
+ "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.2.0.tgz",
+ "integrity": "sha512-7sZVj7hOcytQrPE17ixjzul9ih81IfXGcEZvr7fT77qy7Hm5rbMjxqSYxCTf3kAyBFRSLq/E8GTapPAjk2coOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@angular-devkit/core": "20.2.0",
+ "@angular-devkit/schematics": "20.2.0",
+ "jsonc-parser": "3.3.1"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0",
+ "npm": "^6.11.0 || ^7.5.6 || >=8.0.0",
+ "yarn": ">= 1.13.0"
+ }
+ },
+ "node_modules/@sigstore/bundle": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.1.0.tgz",
+ "integrity": "sha512-Mm1E3/CmDDCz3nDhFKTuYdB47EdRFRQMOE/EAbiG1MJW77/w1b3P7Qx7JSrVJs8PfwOLOVcKQCHErIwCTyPbag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.4.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@sigstore/core": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@sigstore/core/-/core-2.0.0.tgz",
+ "integrity": "sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@sigstore/protobuf-specs": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.4.3.tgz",
+ "integrity": "sha512-fk2zjD9117RL9BjqEwF7fwv7Q/P9yGsMV4MUJZ/DocaQJ6+3pKr+syBq1owU5Q5qGw5CUbXzm+4yJ2JVRDQeSA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@sigstore/sign": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-3.1.0.tgz",
+ "integrity": "sha512-knzjmaOHOov1Ur7N/z4B1oPqZ0QX5geUfhrVaqVlu+hl0EAoL4o+l0MSULINcD5GCWe3Z0+YJO8ues6vFlW0Yw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^3.1.0",
+ "@sigstore/core": "^2.0.0",
+ "@sigstore/protobuf-specs": "^0.4.0",
+ "make-fetch-happen": "^14.0.2",
+ "proc-log": "^5.0.0",
+ "promise-retry": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@sigstore/tuf": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-3.1.1.tgz",
+ "integrity": "sha512-eFFvlcBIoGwVkkwmTi/vEQFSva3xs5Ot3WmBcjgjVdiaoelBLQaQ/ZBfhlG0MnG0cmTYScPpk7eDdGDWUcFUmg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/protobuf-specs": "^0.4.1",
+ "tuf-js": "^3.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@sigstore/verify": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@sigstore/verify/-/verify-2.1.1.tgz",
+ "integrity": "sha512-hVJD77oT67aowHxwT4+M6PGOp+E2LtLdTK3+FC0lBO9T7sYwItDMXZ7Z07IDCvR1M717a4axbIWckrW67KMP/w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^3.1.0",
+ "@sigstore/core": "^2.0.0",
+ "@sigstore/protobuf-specs": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tufjs/canonical-json": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz",
+ "integrity": "sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/@tufjs/models": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@tufjs/models/-/models-3.0.1.tgz",
+ "integrity": "sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/canonical-json": "2.0.0",
+ "minimatch": "^9.0.5"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/@tufjs/models/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/@tufjs/models/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.10.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.0.tgz",
+ "integrity": "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/cors": {
+ "version": "2.8.19",
+ "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz",
+ "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/jasmine": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-5.1.9.tgz",
+ "integrity": "sha512-8t4HtkW4wxiPVedMpeZ63n3vlWxEIquo/zc1Tm8ElU+SqVV7+D3Na2PWaJUp179AzTragMWVwkMv7mvty0NfyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "24.3.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.3.0.tgz",
+ "integrity": "sha512-aPTXCrfwnDLj4VvXrm+UUCQjNEvJgNA8s5F1cvwQU+3KNltTOkBm1j30uNLyqqPNe7gE3KFzImYoZEfLhp4Yow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.10.0"
+ }
+ },
+ "node_modules/@types/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==",
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-basic-ssl": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz",
+ "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/@yarnpkg/lockfile": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz",
+ "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/abbrev": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-3.0.1.tgz",
+ "integrity": "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
+ "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "^3.0.0",
+ "negotiator": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+ "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/algoliasearch": {
+ "version": "5.35.0",
+ "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.35.0.tgz",
+ "integrity": "sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@algolia/abtesting": "1.1.0",
+ "@algolia/client-abtesting": "5.35.0",
+ "@algolia/client-analytics": "5.35.0",
+ "@algolia/client-common": "5.35.0",
+ "@algolia/client-insights": "5.35.0",
+ "@algolia/client-personalization": "5.35.0",
+ "@algolia/client-query-suggestions": "5.35.0",
+ "@algolia/client-search": "5.35.0",
+ "@algolia/ingestion": "1.35.0",
+ "@algolia/monitoring": "1.35.0",
+ "@algolia/recommend": "5.35.0",
+ "@algolia/requester-browser-xhr": "5.35.0",
+ "@algolia/requester-fetch": "5.35.0",
+ "@algolia/requester-node-http": "5.35.0"
+ },
+ "engines": {
+ "node": ">= 14.0.0"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz",
+ "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ansis": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.1.0.tgz",
+ "integrity": "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "license": "MIT"
+ },
+ "node_modules/base64id": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz",
+ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^4.5.0 || >= 5.9"
+ }
+ },
+ "node_modules/beasties": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.5.tgz",
+ "integrity": "sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "css-select": "^6.0.0",
+ "css-what": "^7.0.0",
+ "dom-serializer": "^2.0.0",
+ "domhandler": "^5.0.3",
+ "htmlparser2": "^10.0.0",
+ "picocolors": "^1.1.1",
+ "postcss": "^8.4.49",
+ "postcss-media-query-parser": "^0.2.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
+ "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "^3.1.2",
+ "content-type": "^1.0.5",
+ "debug": "^4.4.0",
+ "http-errors": "^2.0.0",
+ "iconv-lite": "^0.6.3",
+ "on-finished": "^2.4.1",
+ "qs": "^6.14.0",
+ "raw-body": "^3.0.0",
+ "type-is": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/boolbase": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
+ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==",
+ "license": "ISC"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.25.3",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.3.tgz",
+ "integrity": "sha512-cDGv1kkDI4/0e5yON9yM5G/0A5u8sf5TnmdX5C9qHzI9PPu++sQ9zjm1k9NiOrf3riY4OkK0zSGqfvJyJsgCBQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001735",
+ "electron-to-chromium": "^1.5.204",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/cacache": {
+ "version": "19.0.1",
+ "resolved": "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz",
+ "integrity": "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/fs": "^4.0.0",
+ "fs-minipass": "^3.0.0",
+ "glob": "^10.2.2",
+ "lru-cache": "^10.0.1",
+ "minipass": "^7.0.3",
+ "minipass-collect": "^2.0.1",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "p-map": "^7.0.2",
+ "ssri": "^12.0.0",
+ "tar": "^7.4.3",
+ "unique-filename": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/cacache/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/cacache/node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cacache/node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/cacache/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/cacache/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/cacache/node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/cacache/node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/cacache/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001737",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001737.tgz",
+ "integrity": "sha512-BiloLiXtQNrY5UyF0+1nSJLXUENuhka2pzy2Fx5pGxqavdrxSCW4U6Pn/PoG3Efspi2frRbHpBV2XsrPE6EDlw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.0.tgz",
+ "integrity": "sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.0.tgz",
+ "integrity": "sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz",
+ "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/connect": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz",
+ "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==",
+ "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/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/connect/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/connect/node_modules/finalhandler": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz",
+ "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==",
+ "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/connect/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/connect/node_modules/on-finished": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
+ "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/connect/node_modules/statuses": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
+ "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
+ "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "1.9.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
+ "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
+ "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.6.0"
+ }
+ },
+ "node_modules/cors": {
+ "version": "2.8.5",
+ "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
+ "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4",
+ "vary": "^1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/css-select": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz",
+ "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^7.0.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.2",
+ "nth-check": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/css-what": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz",
+ "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/custom-event": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/custom-event/-/custom-event-1.0.1.tgz",
+ "integrity": "sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/date-format": {
+ "version": "4.0.14",
+ "resolved": "https://registry.npmjs.org/date-format/-/date-format-4.0.14.tgz",
+ "integrity": "sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/di": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/di/-/di-0.0.1.tgz",
+ "integrity": "sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dom-serialize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/dom-serialize/-/dom-serialize-2.2.1.tgz",
+ "integrity": "sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ==",
+ "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": "2.0.0",
+ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
+ "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.2",
+ "entities": "^4.2.0"
+ },
+ "funding": {
+ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+ }
+ },
+ "node_modules/domelementtype": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
+ "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/domhandler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
+ "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "domelementtype": "^2.3.0"
+ },
+ "engines": {
+ "node": ">= 4"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domhandler?sponsor=1"
+ }
+ },
+ "node_modules/domutils": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
+ "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dom-serializer": "^2.0.0",
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/domutils?sponsor=1"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "license": "MIT"
+ },
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.209",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.209.tgz",
+ "integrity": "sha512-Xoz0uMrim9ZETCQt8UgM5FxQF9+imA7PBpokoGcZloA1uw2LeHzTlip5cb5KOAsXZLjh/moN2vReN3ZjJmjI9A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/encoding": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
+ "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "iconv-lite": "^0.6.2"
+ }
+ },
+ "node_modules/engine.io": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz",
+ "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/cors": "^2.8.12",
+ "@types/node": ">=10.0.0",
+ "accepts": "~1.3.4",
+ "base64id": "2.0.0",
+ "cookie": "~0.7.2",
+ "cors": "~2.8.5",
+ "debug": "~4.3.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.17.1"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/engine.io/node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/engine.io/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/engine.io/node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ent": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/ent/-/ent-2.2.2.tgz",
+ "integrity": "sha512-kKvD1tO6BM+oK9HzCPpUdRb4vKFQY/FPTFmurMvh6LlN68VMrdj77w8yp51/kDbpkFOS9J8w5W6zIzgM2H8/hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "punycode": "^1.4.1",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.9",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz",
+ "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.9",
+ "@esbuild/android-arm": "0.25.9",
+ "@esbuild/android-arm64": "0.25.9",
+ "@esbuild/android-x64": "0.25.9",
+ "@esbuild/darwin-arm64": "0.25.9",
+ "@esbuild/darwin-x64": "0.25.9",
+ "@esbuild/freebsd-arm64": "0.25.9",
+ "@esbuild/freebsd-x64": "0.25.9",
+ "@esbuild/linux-arm": "0.25.9",
+ "@esbuild/linux-arm64": "0.25.9",
+ "@esbuild/linux-ia32": "0.25.9",
+ "@esbuild/linux-loong64": "0.25.9",
+ "@esbuild/linux-mips64el": "0.25.9",
+ "@esbuild/linux-ppc64": "0.25.9",
+ "@esbuild/linux-riscv64": "0.25.9",
+ "@esbuild/linux-s390x": "0.25.9",
+ "@esbuild/linux-x64": "0.25.9",
+ "@esbuild/netbsd-arm64": "0.25.9",
+ "@esbuild/netbsd-x64": "0.25.9",
+ "@esbuild/openbsd-arm64": "0.25.9",
+ "@esbuild/openbsd-x64": "0.25.9",
+ "@esbuild/openharmony-arm64": "0.25.9",
+ "@esbuild/sunos-x64": "0.25.9",
+ "@esbuild/win32-arm64": "0.25.9",
+ "@esbuild/win32-ia32": "0.25.9",
+ "@esbuild/win32-x64": "0.25.9"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eventsource": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
+ "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eventsource-parser": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/eventsource-parser": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.5.tgz",
+ "integrity": "sha512-bSRG85ZrMdmWtm7qkF9He9TNRzc/Bm99gEJMaQoHJ9E6Kv9QBbsldh2oMj7iXmYNEAVvNgvv5vPorG6W+XtBhQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/exponential-backoff": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.2.tgz",
+ "integrity": "sha512-8QxYTVXUkuy7fIIoitQkPwGonB8F3Zj8eEO8Sqg9Zv/bkI7RJAzowee4gr81Hak/dUTpA2Z7VfQgoijjPNlUZA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/express": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
+ "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "^2.0.0",
+ "body-parser": "^2.2.0",
+ "content-disposition": "^1.0.0",
+ "content-type": "^1.0.5",
+ "cookie": "^0.7.1",
+ "cookie-signature": "^1.2.1",
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "finalhandler": "^2.1.0",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "merge-descriptors": "^2.0.0",
+ "mime-types": "^3.0.0",
+ "on-finished": "^2.4.1",
+ "once": "^1.4.0",
+ "parseurl": "^1.3.3",
+ "proxy-addr": "^2.0.7",
+ "qs": "^6.14.0",
+ "range-parser": "^1.2.1",
+ "router": "^2.2.0",
+ "send": "^1.1.0",
+ "serve-static": "^2.2.0",
+ "statuses": "^2.0.1",
+ "type-is": "^2.0.1",
+ "vary": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express-rate-limit": {
+ "version": "7.5.1",
+ "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
+ "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/express-rate-limit"
+ },
+ "peerDependencies": {
+ "express": ">= 4.11"
+ }
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
+ "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "on-finished": "^2.4.1",
+ "parseurl": "^1.3.3",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.15.11",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz",
+ "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/RubenVerborgh"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependenciesMeta": {
+ "debug": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
+ "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz",
+ "integrity": "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": 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.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Glob versions prior to v9 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.1.1",
+ "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",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob-to-regexp": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
+ "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.0.tgz",
+ "integrity": "sha512-gEf705MZLrDPkbbhi8PnoO4ZwYgKoNL+ISZ3AjZMht2r3N5tuTwncyDi6Fv2/qDnMmZxgs0yI8WDOyR8q3G+SQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^11.1.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/hosted-git-info/node_modules/lru-cache": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz",
+ "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/html-escaper": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+ "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/htmlparser2": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz",
+ "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==",
+ "dev": true,
+ "funding": [
+ "https://github.com/fb55/htmlparser2?sponsor=1",
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fb55"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "domelementtype": "^2.3.0",
+ "domhandler": "^5.0.3",
+ "domutils": "^3.2.1",
+ "entities": "^6.0.0"
+ }
+ },
+ "node_modules/htmlparser2/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
+ "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
+ "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",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
+ "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/http-proxy": {
+ "version": "1.18.1",
+ "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz",
+ "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==",
+ "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": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ignore-walk": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz",
+ "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minimatch": "^10.0.3"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/ignore-walk/node_modules/minimatch": {
+ "version": "10.0.3",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
+ "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/brace-expansion": "^5.0.0"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
+ "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ini": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz",
+ "integrity": "sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/ip-address": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz",
+ "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
+ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz",
+ "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isbinaryfile": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+ "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "license": "ISC"
+ },
+ "node_modules/istanbul-lib-coverage": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+ "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/istanbul-lib-instrument": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz",
+ "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@babel/core": "^7.23.9",
+ "@babel/parser": "^7.23.9",
+ "@istanbuljs/schema": "^0.1.3",
+ "istanbul-lib-coverage": "^3.2.0",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-report": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+ "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "istanbul-lib-coverage": "^3.0.0",
+ "make-dir": "^4.0.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/istanbul-lib-source-maps": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz",
+ "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==",
+ "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",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/istanbul-reports": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz",
+ "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "html-escaper": "^2.0.0",
+ "istanbul-lib-report": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jasmine-core": {
+ "version": "5.8.0",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.8.0.tgz",
+ "integrity": "sha512-Q9dqmpUAfptwyueW3+HqBOkSuYd9I/clZSSfN97wXE/Nr2ROFNCwIBEC1F6kb3QXS9Fcz0LjFYSDQT+BiwjuhA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-parse-even-better-errors": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz",
+ "integrity": "sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-parser": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
+ "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==",
+ "dev": true,
+ "engines": [
+ "node >= 0.2.0"
+ ],
+ "license": "MIT"
+ },
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "license": "MIT",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jwa": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz",
+ "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal-constant-time": "^1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "license": "MIT",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/karma": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/karma/-/karma-6.4.4.tgz",
+ "integrity": "sha512-LrtUxbdvt1gOpo3gxG+VAJlJAEMhbWlM4YrFQgql98FwF7+K8K12LYO4hnDdUkNjeztYrOXEMqgTajSWgmtI/w==",
+ "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.7.2",
+ "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.2.0",
+ "resolved": "https://registry.npmjs.org/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz",
+ "integrity": "sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "which": "^1.2.1"
+ }
+ },
+ "node_modules/karma-chrome-launcher/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
+ "node_modules/karma-coverage": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/karma-coverage/-/karma-coverage-2.2.1.tgz",
+ "integrity": "sha512-yj7hbequkQP2qOSb20GuNSIyE//PgJWHwC2IydLE6XRtsnaflv+/OSGNssPjobYUlhVVagy99TQpqUt3vAUG7A==",
+ "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-coverage/node_modules/istanbul-lib-instrument": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz",
+ "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==",
+ "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/karma-coverage/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/karma-jasmine": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-5.1.0.tgz",
+ "integrity": "sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ==",
+ "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": "2.1.0",
+ "resolved": "https://registry.npmjs.org/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.1.0.tgz",
+ "integrity": "sha512-sPQE1+nlsn6Hwb5t+HHwyy0A1FNCVKuL1192b+XNauMYWThz2kweiBVW1DqloRpVvZIJkIoHVB7XRpK78n1xbQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "jasmine-core": "^4.0.0 || ^5.0.0",
+ "karma": "^6.0.0",
+ "karma-jasmine": "^5.0.0"
+ }
+ },
+ "node_modules/karma-jasmine/node_modules/jasmine-core": {
+ "version": "4.6.1",
+ "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-4.6.1.tgz",
+ "integrity": "sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/karma/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/karma/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "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/karma/node_modules/body-parser": {
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "content-type": "~1.0.5",
+ "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.13.0",
+ "raw-body": "2.5.2",
+ "type-is": "~1.6.18",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/karma/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "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"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/karma/node_modules/cliui": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+ "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^7.0.0"
+ }
+ },
+ "node_modules/karma/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/karma/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/karma/node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/karma/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/karma/node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/karma/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/karma/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/karma/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/karma/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/karma/node_modules/qs": {
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.0.6"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/karma/node_modules/raw-body": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
+ "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
+ "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/karma/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/karma/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/karma/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "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/karma/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/karma/node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/karma/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "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/karma/node_modules/yargs": {
+ "version": "16.2.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+ "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+ "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",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
+ "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz",
+ "integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/listr2/node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/listr2/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/lmdb": {
+ "version": "3.4.2",
+ "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.4.2.tgz",
+ "integrity": "sha512-nwVGUfTBUwJKXd6lRV8pFNfnrCC1+l49ESJRM19t/tFb/97QfJEixe5DYRvug5JO7DSFKoKaVy7oGMt5rVqZvg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "msgpackr": "^1.11.2",
+ "node-addon-api": "^6.1.0",
+ "node-gyp-build-optional-packages": "5.2.2",
+ "ordered-binary": "^1.5.3",
+ "weak-lru-cache": "^1.2.2"
+ },
+ "bin": {
+ "download-lmdb-prebuilds": "bin/download-prebuilds.js"
+ },
+ "optionalDependencies": {
+ "@lmdb/lmdb-darwin-arm64": "3.4.2",
+ "@lmdb/lmdb-darwin-x64": "3.4.2",
+ "@lmdb/lmdb-linux-arm": "3.4.2",
+ "@lmdb/lmdb-linux-arm64": "3.4.2",
+ "@lmdb/lmdb-linux-x64": "3.4.2",
+ "@lmdb/lmdb-win32-arm64": "3.4.2",
+ "@lmdb/lmdb-win32-x64": "3.4.2"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz",
+ "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.3.0",
+ "is-unicode-supported": "^1.3.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-symbols/node_modules/is-unicode-supported": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz",
+ "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/log4js": {
+ "version": "6.9.1",
+ "resolved": "https://registry.npmjs.org/log4js/-/log4js-6.9.1.tgz",
+ "integrity": "sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "flatted": "^3.2.7",
+ "rfdc": "^1.3.0",
+ "streamroller": "^3.1.5"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/make-dir": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+ "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/make-fetch-happen": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz",
+ "integrity": "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/agent": "^3.0.0",
+ "cacache": "^19.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^4.0.0",
+ "minipass-flush": "^1.0.5",
+ "minipass-pipeline": "^1.2.4",
+ "negotiator": "^1.0.0",
+ "proc-log": "^5.0.0",
+ "promise-retry": "^2.0.1",
+ "ssri": "^12.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
+ "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
+ "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
+ "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "^1.54.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minipass-collect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz",
+ "integrity": "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minipass-fetch": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz",
+ "integrity": "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.0.3",
+ "minipass-sized": "^1.0.3",
+ "minizlib": "^3.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ },
+ "optionalDependencies": {
+ "encoding": "^0.1.13"
+ }
+ },
+ "node_modules/minipass-flush": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz",
+ "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minipass-flush/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-flush/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minipass-pipeline": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz",
+ "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-pipeline/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-pipeline/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minipass-sized": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz",
+ "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-sized/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minipass-sized/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/msgpackr": {
+ "version": "1.11.5",
+ "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz",
+ "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "optionalDependencies": {
+ "msgpackr-extract": "^3.0.2"
+ }
+ },
+ "node_modules/msgpackr-extract": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz",
+ "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "node-gyp-build-optional-packages": "5.2.2"
+ },
+ "bin": {
+ "download-msgpackr-prebuilds": "bin/download-prebuilds.js"
+ },
+ "optionalDependencies": {
+ "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3",
+ "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3",
+ "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3",
+ "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3",
+ "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3",
+ "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3"
+ }
+ },
+ "node_modules/mute-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
+ "integrity": "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/negotiator": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
+ "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
+ "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/node-gyp": {
+ "version": "11.4.2",
+ "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.4.2.tgz",
+ "integrity": "sha512-3gD+6zsrLQH7DyYOUIutaauuXrcyxeTPyQuZQCQoNPZMHMMS5m4y0xclNpvYzoK3VNzuyxT6eF4mkIL4WSZ1eQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "env-paths": "^2.2.0",
+ "exponential-backoff": "^3.1.1",
+ "graceful-fs": "^4.2.6",
+ "make-fetch-happen": "^14.0.3",
+ "nopt": "^8.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.3.5",
+ "tar": "^7.4.3",
+ "tinyglobby": "^0.2.12",
+ "which": "^5.0.0"
+ },
+ "bin": {
+ "node-gyp": "bin/node-gyp.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/node-gyp-build-optional-packages": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
+ "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.1"
+ },
+ "bin": {
+ "node-gyp-build-optional-packages": "bin.js",
+ "node-gyp-build-optional-packages-optional": "optional.js",
+ "node-gyp-build-optional-packages-test": "build-test.js"
+ }
+ },
+ "node_modules/node-gyp/node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/node-gyp/node_modules/isexe": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz",
+ "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/node-gyp/node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/node-gyp/node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/node-gyp/node_modules/which": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/which/-/which-5.0.0.tgz",
+ "integrity": "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^3.1.1"
+ },
+ "bin": {
+ "node-which": "bin/which.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/node-gyp/node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/node-html-parser": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.0.1.tgz",
+ "integrity": "sha512-KGtmPY2kS0thCWGK0VuPyOS+pBKhhe8gXztzA2ilAOhbUbxa9homF1bOyKvhGzMLXUoRds9IOmr/v5lr/lqNmA==",
+ "license": "MIT",
+ "dependencies": {
+ "css-select": "^5.1.0",
+ "he": "1.2.0"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/css-select": {
+ "version": "5.2.2",
+ "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz",
+ "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0",
+ "css-what": "^6.1.0",
+ "domhandler": "^5.0.2",
+ "domutils": "^3.0.1",
+ "nth-check": "^2.0.1"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/node-html-parser/node_modules/css-what": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
+ "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">= 6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/fb55"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nopt": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/nopt/-/nopt-8.1.0.tgz",
+ "integrity": "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "abbrev": "^3.0.0"
+ },
+ "bin": {
+ "nopt": "bin/nopt.js"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-bundled": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz",
+ "integrity": "sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-normalize-package-bin": "^4.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-install-checks": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz",
+ "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "semver": "^7.1.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-normalize-package-bin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz",
+ "integrity": "sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-package-arg": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.0.tgz",
+ "integrity": "sha512-+t2etZAGcB7TbbLHfDwooV9ppB2LhhcT6A+L9cahsf9mEUAoQ6CktLEVvEnpD0N5CkX7zJqnPGaFtoQDy9EkHQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^9.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^6.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/npm-packlist": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.1.tgz",
+ "integrity": "sha512-vaC03b2PqJA6QqmwHi1jNU8fAPXEnnyv4j/W4PVfgm24C4/zZGSVut3z0YUeN0WIFCo1oGOL02+6LbvFK7JL4Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "ignore-walk": "^8.0.0"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/npm-pick-manifest": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz",
+ "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "npm-install-checks": "^7.1.0",
+ "npm-normalize-package-bin": "^4.0.0",
+ "npm-package-arg": "^12.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-pick-manifest/node_modules/hosted-git-info": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz",
+ "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-pick-manifest/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/npm-pick-manifest/node_modules/npm-package-arg": {
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz",
+ "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^8.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^6.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-registry-fetch": {
+ "version": "18.0.2",
+ "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz",
+ "integrity": "sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/redact": "^3.0.0",
+ "jsonparse": "^1.3.1",
+ "make-fetch-happen": "^14.0.0",
+ "minipass": "^7.0.2",
+ "minipass-fetch": "^4.0.0",
+ "minizlib": "^3.0.1",
+ "npm-package-arg": "^12.0.0",
+ "proc-log": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/hosted-git-info": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz",
+ "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/npm-registry-fetch/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/npm-registry-fetch/node_modules/npm-package-arg": {
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz",
+ "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^8.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^6.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/nth-check": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
+ "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "boolbase": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/nth-check?sponsor=1"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ora": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
+ "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^5.3.0",
+ "cli-cursor": "^5.0.0",
+ "cli-spinners": "^2.9.2",
+ "is-interactive": "^2.0.0",
+ "is-unicode-supported": "^2.0.0",
+ "log-symbols": "^6.0.0",
+ "stdin-discarder": "^0.2.2",
+ "string-width": "^7.2.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ordered-binary": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.0.tgz",
+ "integrity": "sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/orderedmap": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
+ "license": "MIT"
+ },
+ "node_modules/p-map": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz",
+ "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/pacote": {
+ "version": "21.0.0",
+ "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.0.tgz",
+ "integrity": "sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@npmcli/git": "^6.0.0",
+ "@npmcli/installed-package-contents": "^3.0.0",
+ "@npmcli/package-json": "^6.0.0",
+ "@npmcli/promise-spawn": "^8.0.0",
+ "@npmcli/run-script": "^9.0.0",
+ "cacache": "^19.0.0",
+ "fs-minipass": "^3.0.0",
+ "minipass": "^7.0.2",
+ "npm-package-arg": "^12.0.0",
+ "npm-packlist": "^10.0.0",
+ "npm-pick-manifest": "^10.0.0",
+ "npm-registry-fetch": "^18.0.0",
+ "proc-log": "^5.0.0",
+ "promise-retry": "^2.0.1",
+ "sigstore": "^3.0.0",
+ "ssri": "^12.0.0",
+ "tar": "^6.1.11"
+ },
+ "bin": {
+ "pacote": "bin/index.js"
+ },
+ "engines": {
+ "node": "^20.17.0 || >=22.9.0"
+ }
+ },
+ "node_modules/pacote/node_modules/hosted-git-info": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz",
+ "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/pacote/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/pacote/node_modules/npm-package-arg": {
+ "version": "12.0.2",
+ "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz",
+ "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "hosted-git-info": "^8.0.0",
+ "proc-log": "^5.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-name": "^6.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz",
+ "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-html-rewriting-stream": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz",
+ "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0",
+ "parse5": "^8.0.0",
+ "parse5-sax-parser": "^8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5-html-rewriting-stream/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/parse5-sax-parser": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz",
+ "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parse5": "^8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/parse5/node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "license": "ISC"
+ },
+ "node_modules/path-to-regexp": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
+ "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/piscina": {
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.3.tgz",
+ "integrity": "sha512-0u3N7H4+hbr40KjuVn2uNhOcthu/9usKhnw5vT3J7ply79v3D3M8naI00el9Klcy16x557VsEkkUQaHCWFXC/g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.x"
+ },
+ "optionalDependencies": {
+ "@napi-rs/nice": "^1.0.4"
+ }
+ },
+ "node_modules/pkce-challenge": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
+ "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-media-query-parser": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz",
+ "integrity": "sha512-3sOlxmbKcSHMjlUXQZKQ06jOswE7oVkXPxmZdoB1r5l0q6gTFTQSHxNxOrCccElbW7dxNytifNEo8qidX2Vsig==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/proc-log": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz",
+ "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/prosemirror-commands": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
+ "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.10.2"
+ }
+ },
+ "node_modules/prosemirror-dropcursor": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
+ "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0",
+ "prosemirror-view": "^1.1.0"
+ }
+ },
+ "node_modules/prosemirror-gapcursor": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
+ "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-keymap": "^1.0.0",
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-view": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-history": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz",
+ "integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.2.2",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.31.0",
+ "rope-sequence": "^1.3.0"
+ }
+ },
+ "node_modules/prosemirror-inputrules": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.0.tgz",
+ "integrity": "sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.0.0"
+ }
+ },
+ "node_modules/prosemirror-keymap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
+ "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "w3c-keyname": "^2.2.0"
+ }
+ },
+ "node_modules/prosemirror-model": {
+ "version": "1.25.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.1.tgz",
+ "integrity": "sha512-AUvbm7qqmpZa5d9fPKMvH1Q5bqYQvAZWOGRvxsB6iFLyycvC9MwNemNVjHVrWgjaoxAfY8XVg7DbvQ/qxvI9Eg==",
+ "license": "MIT",
+ "dependencies": {
+ "orderedmap": "^2.0.0"
+ }
+ },
+ "node_modules/prosemirror-schema-list": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
+ "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.7.3"
+ }
+ },
+ "node_modules/prosemirror-state": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
+ "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.27.0"
+ }
+ },
+ "node_modules/prosemirror-tables": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.7.1.tgz",
+ "integrity": "sha512-eRQ97Bf+i9Eby99QbyAiyov43iOKgWa7QCGly+lrDt7efZ1v8NWolhXiB43hSDGIXT1UXgbs4KJN3a06FGpr1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-keymap": "^1.2.2",
+ "prosemirror-model": "^1.25.0",
+ "prosemirror-state": "^1.4.3",
+ "prosemirror-transform": "^1.10.3",
+ "prosemirror-view": "^1.39.1"
+ }
+ },
+ "node_modules/prosemirror-transform": {
+ "version": "1.10.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz",
+ "integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.21.0"
+ }
+ },
+ "node_modules/prosemirror-view": {
+ "version": "1.39.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.39.3.tgz",
+ "integrity": "sha512-bY/7kg0LzRE7ytR0zRdSMWX3sknEjw68l836ffLPMh0OG3OYnNuBDUSF3v0vjvnzgYjgY9ZH/RypbARURlcMFA==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.20.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/qjobs": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/qjobs/-/qjobs-1.2.0.tgz",
+ "integrity": "sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.9"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
+ "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "http-errors": "2.0.0",
+ "iconv-lite": "0.6.3",
+ "unpipe": "1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/reflect-metadata": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz",
+ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/requires-port": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
+ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rimraf": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+ "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+ "deprecated": "Rimraf versions prior to v4 are no longer supported",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "glob": "^7.1.3"
+ },
+ "bin": {
+ "rimraf": "bin.js"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/rolldown": {
+ "version": "1.0.0-beta.32",
+ "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.32.tgz",
+ "integrity": "sha512-vxI2sPN07MMaoYKlFrVva5qZ1Y7DAZkgp7MQwTnyHt4FUMz9Sh+YeCzNFV9JYHI6ZNwoGWLCfCViE3XVsRC1cg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@oxc-project/runtime": "=0.81.0",
+ "@oxc-project/types": "=0.81.0",
+ "@rolldown/pluginutils": "1.0.0-beta.32",
+ "ansis": "^4.0.0"
+ },
+ "bin": {
+ "rolldown": "bin/cli.mjs"
+ },
+ "optionalDependencies": {
+ "@rolldown/binding-android-arm64": "1.0.0-beta.32",
+ "@rolldown/binding-darwin-arm64": "1.0.0-beta.32",
+ "@rolldown/binding-darwin-x64": "1.0.0-beta.32",
+ "@rolldown/binding-freebsd-x64": "1.0.0-beta.32",
+ "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.32",
+ "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.32",
+ "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.32",
+ "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.32",
+ "@rolldown/binding-linux-x64-musl": "1.0.0-beta.32",
+ "@rolldown/binding-openharmony-arm64": "1.0.0-beta.32",
+ "@rolldown/binding-wasm32-wasi": "1.0.0-beta.32",
+ "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.32",
+ "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.32",
+ "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.32"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.49.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.49.0.tgz",
+ "integrity": "sha512-3IVq0cGJ6H7fKXXEdVt+RcYvRCt8beYY9K1760wGQwSAHZcS9eot1zDG5axUbcp/kWRi5zKIIDX8MoKv/TzvZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.49.0",
+ "@rollup/rollup-android-arm64": "4.49.0",
+ "@rollup/rollup-darwin-arm64": "4.49.0",
+ "@rollup/rollup-darwin-x64": "4.49.0",
+ "@rollup/rollup-freebsd-arm64": "4.49.0",
+ "@rollup/rollup-freebsd-x64": "4.49.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.49.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.49.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.49.0",
+ "@rollup/rollup-linux-arm64-musl": "4.49.0",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.49.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.49.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.49.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.49.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.49.0",
+ "@rollup/rollup-linux-x64-gnu": "4.49.0",
+ "@rollup/rollup-linux-x64-musl": "4.49.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.49.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.49.0",
+ "@rollup/rollup-win32-x64-msvc": "4.49.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rope-sequence": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
+ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
+ "license": "MIT"
+ },
+ "node_modules/router": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
+ "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.4.0",
+ "depd": "^2.0.0",
+ "is-promise": "^4.0.0",
+ "parseurl": "^1.3.3",
+ "path-to-regexp": "^8.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sass": {
+ "version": "1.90.0",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz",
+ "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^5.0.2",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
+ "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.5",
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "etag": "^1.8.1",
+ "fresh": "^2.0.0",
+ "http-errors": "^2.0.0",
+ "mime-types": "^3.0.1",
+ "ms": "^2.1.3",
+ "on-finished": "^2.4.1",
+ "range-parser": "^1.2.1",
+ "statuses": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
+ "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "^2.0.0",
+ "escape-html": "^1.0.3",
+ "parseurl": "^1.3.3",
+ "send": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sigstore": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-3.1.0.tgz",
+ "integrity": "sha512-ZpzWAFHIFqyFE56dXqgX/DkDRZdz+rRcjoIk/RQU4IX0wiCv1l8S7ZrXDHcCc+uaf+6o7w3h2l3g6GYG5TKN9Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@sigstore/bundle": "^3.1.0",
+ "@sigstore/core": "^2.0.0",
+ "@sigstore/protobuf-specs": "^0.4.0",
+ "@sigstore/sign": "^3.1.0",
+ "@sigstore/tuf": "^3.1.0",
+ "@sigstore/verify": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socket.io": {
+ "version": "4.8.1",
+ "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz",
+ "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.4",
+ "base64id": "~2.0.0",
+ "cors": "~2.8.5",
+ "debug": "~4.3.2",
+ "engine.io": "~6.6.0",
+ "socket.io-adapter": "~2.5.2",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.2.0"
+ }
+ },
+ "node_modules/socket.io-adapter": {
+ "version": "2.5.5",
+ "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz",
+ "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "~4.3.4",
+ "ws": "~8.17.1"
+ }
+ },
+ "node_modules/socket.io-adapter/node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.4",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz",
+ "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.3.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-parser/node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/socket.io/node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/socket.io/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socket.io/node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/socks": {
+ "version": "2.8.7",
+ "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz",
+ "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ip-address": "^10.0.1",
+ "smart-buffer": "^4.2.0"
+ },
+ "engines": {
+ "node": ">= 10.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/socks-proxy-agent": {
+ "version": "8.0.5",
+ "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz",
+ "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "^4.3.4",
+ "socks": "^2.8.3"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "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",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "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.5.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+ "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+ "dev": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.22",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz",
+ "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/ssri": {
+ "version": "12.0.0",
+ "resolved": "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz",
+ "integrity": "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/stdin-discarder": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz",
+ "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/streamroller": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/streamroller/-/streamroller-3.1.5.tgz",
+ "integrity": "sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "date-format": "^4.0.14",
+ "debug": "^4.3.4",
+ "fs-extra": "^8.1.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "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-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "chownr": "^2.0.0",
+ "fs-minipass": "^2.0.0",
+ "minipass": "^5.0.0",
+ "minizlib": "^2.1.1",
+ "mkdirp": "^1.0.3",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar/node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar/node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar/node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/tar/node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar/node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar/node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
+ "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tuf-js": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.1.0.tgz",
+ "integrity": "sha512-3T3T04WzowbwV2FDiGXBbr81t64g1MUGGJRgT4x5o97N+8ArdhVCAF9IxFrxuSJmM3E5Asn7nKHkao0ibcZXAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tufjs/models": "3.0.1",
+ "debug": "^4.4.1",
+ "make-fetch-happen": "^14.0.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
+ "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "content-type": "^1.0.5",
+ "media-typer": "^1.1.0",
+ "mime-types": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/ua-parser-js": {
+ "version": "0.7.41",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.41.tgz",
+ "integrity": "sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/ua-parser-js"
+ },
+ {
+ "type": "paypal",
+ "url": "https://paypal.me/faisalman"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/faisalman"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "ua-parser-js": "script/cli.js"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
+ "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unique-filename": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz",
+ "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "unique-slug": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/unique-slug": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz",
+ "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "imurmurhash": "^0.1.4"
+ },
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/uri-js/node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "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": "6.0.2",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz",
+ "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^18.17.0 || >=20.5.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.2.tgz",
+ "integrity": "sha512-J0SQBPlQiEXAF7tajiH+rUooJPo0l8KQgyg4/aMunNtrOa7bwuZJsJbDWzeljqQpgftxuq5yNJxQ91O9ts29UQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.6",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.14"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-2.0.1.tgz",
+ "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
+ "node_modules/watchpack": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz",
+ "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob-to-regexp": "^0.4.1",
+ "graceful-fs": "^4.1.2"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/weak-lru-cache": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz",
+ "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "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/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "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-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "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-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "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/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "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/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "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/wrap-ansi/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/ws": {
+ "version": "8.17.1",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz",
+ "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
+ "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/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "18.0.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz",
+ "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^9.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "string-width": "^7.2.0",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^22.0.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "22.0.0",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz",
+ "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^20.19.0 || ^22.12.0 || >=23"
+ }
+ },
+ "node_modules/yoctocolors-cjs": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+ "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.76",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
+ "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.24.6",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
+ "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
+ "dev": true,
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.24.1"
+ }
+ },
+ "node_modules/zone.js": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz",
+ "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==",
+ "license": "MIT"
+ }
+ }
+}
diff --git a/APP/package.json b/APP/package.json
new file mode 100644
index 0000000..f94f1c4
--- /dev/null
+++ b/APP/package.json
@@ -0,0 +1,89 @@
+{
+ "name": "RBJ.Identity.App",
+ "version": "0.0.0",
+ "scripts": {
+ "ng": "ng",
+ "start": "ng serve",
+ "build": "ng build",
+ "watch": "ng build --watch --configuration development",
+ "test": "ng test"
+ },
+ "prettier": {
+ "printWidth": 100,
+ "singleQuote": true,
+ "overrides": [
+ {
+ "files": "*.html",
+ "options": {
+ "parser": "angular"
+ }
+ }
+ ]
+ },
+ "private": true,
+ "dependencies": {
+ "@angular/animations": "^20.1.0",
+ "@angular/common": "^20.1.0",
+ "@angular/compiler": "^20.1.0",
+ "@angular/core": "^20.1.0",
+ "@angular/forms": "^20.1.0",
+ "@angular/localize": "^20.1.6",
+ "@angular/platform-browser": "^20.1.0",
+ "@angular/router": "^20.1.0",
+ "@progress/kendo-angular-buttons": "^20.0.0",
+ "@progress/kendo-angular-charts": "^20.0.0",
+ "@progress/kendo-angular-common": "^20.0.0",
+ "@progress/kendo-angular-conversational-ui": "^20.0.0",
+ "@progress/kendo-angular-dateinputs": "^20.0.0",
+ "@progress/kendo-angular-dialog": "^20.0.0",
+ "@progress/kendo-angular-dropdowns": "^20.0.0",
+ "@progress/kendo-angular-editor": "^20.0.0",
+ "@progress/kendo-angular-excel-export": "^20.0.3",
+ "@progress/kendo-angular-gauges": "^20.0.0",
+ "@progress/kendo-angular-grid": "^20.0.0",
+ "@progress/kendo-angular-icons": "^20.0.0",
+ "@progress/kendo-angular-indicators": "^20.0.0",
+ "@progress/kendo-angular-inputs": "^20.0.0",
+ "@progress/kendo-angular-intl": "^20.0.0",
+ "@progress/kendo-angular-l10n": "^20.0.0",
+ "@progress/kendo-angular-label": "^20.0.0",
+ "@progress/kendo-angular-layout": "^20.0.0",
+ "@progress/kendo-angular-listview": "^20.0.0",
+ "@progress/kendo-angular-map": "^20.0.0",
+ "@progress/kendo-angular-menu": "^20.0.0",
+ "@progress/kendo-angular-navigation": "^20.0.0",
+ "@progress/kendo-angular-pager": "^20.0.0",
+ "@progress/kendo-angular-pdf-export": "^20.0.3",
+ "@progress/kendo-angular-popup": "^20.0.0",
+ "@progress/kendo-angular-progressbar": "^20.0.0",
+ "@progress/kendo-angular-scrollview": "^20.0.0",
+ "@progress/kendo-angular-toolbar": "^20.0.3",
+ "@progress/kendo-angular-tooltip": "^20.0.0",
+ "@progress/kendo-angular-treeview": "^20.0.0",
+ "@progress/kendo-angular-upload": "^20.0.0",
+ "@progress/kendo-angular-utils": "^20.0.0",
+ "@progress/kendo-data-query": "^1.7.1",
+ "@progress/kendo-drawing": "^1.22.0",
+ "@progress/kendo-licensing": "^1.7.0",
+ "@progress/kendo-svg-icons": "^4.5.0",
+ "@progress/kendo-theme-default": "^12.0.0",
+ "@progress/kendo-theme-utils": "^12.0.0",
+ "rxjs": "~7.8.0",
+ "tslib": "^2.3.0",
+ "zone.js": "~0.15.0"
+ },
+ "devDependencies": {
+ "@angular/build": "^20.1.6",
+ "@angular/cli": "^20.1.6",
+ "@angular/compiler-cli": "^20.1.0",
+ "@angular/localize": "^20.2.1",
+ "@types/jasmine": "~5.1.0",
+ "jasmine-core": "~5.8.0",
+ "karma": "~6.4.0",
+ "karma-chrome-launcher": "~3.2.0",
+ "karma-coverage": "~2.2.0",
+ "karma-jasmine": "~5.1.0",
+ "karma-jasmine-html-reporter": "~2.1.0",
+ "typescript": "~5.8.2"
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/app.config.ts b/APP/src/app/app.config.ts
new file mode 100644
index 0000000..8ecee26
--- /dev/null
+++ b/APP/src/app/app.config.ts
@@ -0,0 +1,16 @@
+import { ApplicationConfig } from '@angular/core';
+import { provideRouter } from '@angular/router';
+import { provideAnimations } from '@angular/platform-browser/animations';
+import { provideHttpClient, withInterceptors } from '@angular/common/http';
+
+import { routes } from './app.routes';
+import { authInterceptor } from './core/interceptors/auth.interceptor';
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideRouter(routes),
+ provideAnimations(),
+ provideHttpClient(withInterceptors([authInterceptor]))
+ ]
+};
+
diff --git a/APP/src/app/app.html b/APP/src/app/app.html
new file mode 100644
index 0000000..09af448
--- /dev/null
+++ b/APP/src/app/app.html
@@ -0,0 +1,2 @@
+
+
\ No newline at end of file
diff --git a/APP/src/app/app.routes.ts b/APP/src/app/app.routes.ts
new file mode 100644
index 0000000..768dba1
--- /dev/null
+++ b/APP/src/app/app.routes.ts
@@ -0,0 +1,27 @@
+import { Routes } from '@angular/router';
+import { DashboardComponent } from './portals/user-portal/pages/dashboard/dashboard.component';
+import { LoginPage } from './features/login-page/login-page';
+import { UserPortalComponent } from './portals/user-portal/user-portal.component';
+import { AuthGuard } from './core/guards/auth.guard';
+
+export const routes: Routes = [
+ // Public routes
+ { path: 'login', component: LoginPage },
+
+ // Keep the startup surface intentionally small: login + guarded mock dashboard.
+ {
+ path: 'user-portal',
+ component: UserPortalComponent,
+ canActivate: [AuthGuard],
+ children: [
+ { path: '', redirectTo: 'dashboard', pathMatch: 'full' },
+ { path: 'dashboard', component: DashboardComponent }
+ ]
+ },
+
+ { path: '', redirectTo: 'login', pathMatch: 'full' },
+ { path: 'dashboard', redirectTo: 'user-portal/dashboard' },
+
+ // Catch all route - redirect to login
+ { path: '**', redirectTo: 'login' }
+];
diff --git a/APP/src/app/app.scss b/APP/src/app/app.scss
new file mode 100644
index 0000000..f8f183e
--- /dev/null
+++ b/APP/src/app/app.scss
@@ -0,0 +1,53 @@
+/* Global layout styles */
+
+/* Ensure AppBar sections are in one row */
+kendo-appbar {
+ display: flex !important;
+ flex-direction: row !important;
+ align-items: center !important;
+ width: 100% !important;
+}
+
+kendo-appbar-section {
+ display: flex !important;
+ align-items: center !important;
+}
+
+/* Make sure the drawer container takes full height */
+kendo-drawer-container {
+ min-height: calc(100vh - 46px);
+}
+
+/* Global mobile optimizations */
+@media (max-width: 767px) {
+ /* Improve touch targets for mobile */
+ button[kendoButton] {
+ min-height: 44px;
+ min-width: 44px;
+ }
+
+ /* Make drawer content full width on mobile */
+ kendo-drawer-content {
+ width: 100%;
+ }
+
+ /* Prevent horizontal scroll on mobile */
+ body {
+ overflow-x: hidden;
+ }
+}
+
+/* iOS specific fixes */
+@supports (-webkit-touch-callout: none) {
+ /* Prevent iOS zoom on input focus */
+ input,
+ select,
+ textarea {
+ font-size: 16px !important;
+ }
+
+ /* Smooth scrolling for iOS - legacy support for older devices */
+ kendo-drawer-container {
+ overflow-y: auto;
+ }
+}
diff --git a/APP/src/app/app.ts b/APP/src/app/app.ts
new file mode 100644
index 0000000..f706dbc
--- /dev/null
+++ b/APP/src/app/app.ts
@@ -0,0 +1,28 @@
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { RouterOutlet } from '@angular/router';
+import { DialogModule } from '@progress/kendo-angular-dialog';
+import { AuthService } from './shared/services/auth.service';
+
+@Component({
+ selector: 'app-root',
+ standalone: true,
+ imports: [
+ CommonModule,
+ RouterOutlet,
+ DialogModule
+ ],
+ templateUrl: './app.html',
+ styleUrls: ['./app.scss', '../styles.scss'],
+ encapsulation: ViewEncapsulation.None
+})
+export class App implements OnInit {
+ title = 'RBJ Identity';
+
+ constructor(private authService: AuthService) { }
+
+ ngOnInit(): void {
+ // Initialize authentication state from localStorage
+ this.authService.initializeAuth();
+ }
+}
diff --git a/APP/src/app/core/guards/README.md b/APP/src/app/core/guards/README.md
new file mode 100644
index 0000000..e3c54ab
--- /dev/null
+++ b/APP/src/app/core/guards/README.md
@@ -0,0 +1,120 @@
+# Authentication Guard System
+
+This implementation provides a complete authentication system that automatically detects if a user is logged in and redirects to the login page if not.
+
+## Components
+
+### AuthGuard (`auth.guard.ts`)
+- **Purpose**: Protects routes that require authentication
+- **Functionality**:
+ - Checks if user is authenticated using `AuthService.isAuthenticated()`
+ - Stores attempted URL for redirect after login
+ - Redirects to `/login` if not authenticated
+ - Allows access if authenticated
+
+### LoginPage (`login-page.ts`)
+- **Purpose**: Full-page login interface for routing
+- **Features**:
+ - Beautiful landing page with company branding
+ - Integrated login dialog
+ - Demo credentials display
+ - Automatic redirect after successful login
+ - Prevents access if already logged in
+
+## How It Works
+
+### 1. Route Protection
+All protected routes use the `AuthGuard`:
+```typescript
+{ path: 'dashboard', component: Dashboard, canActivate: [AuthGuard] }
+```
+
+### 2. Authentication Flow
+1. User tries to access protected route
+2. `AuthGuard` checks authentication status
+3. If not authenticated:
+ - Stores attempted URL
+ - Redirects to `/login`
+4. If authenticated:
+ - Allows access to requested route
+
+### 3. Login Process
+1. User lands on `/login` page
+2. Clicks "Sign In" button
+3. Login dialog opens with MFA support
+4. After successful login:
+ - User data stored in localStorage
+ - Redirected to originally requested URL or dashboard
+
+### 4. Logout Process
+1. User clicks logout from header dropdown
+2. `AuthService.logout()` clears user data
+3. Redirected to `/login` page
+
+## User State Management
+
+### AuthService Features
+- **Persistent Login**: User stays logged in across browser sessions
+- **State Management**: Reactive user state with RxJS
+- **Redirect URLs**: Remembers where user was trying to go
+- **localStorage**: Automatic persistence and restoration
+
+### Header Integration
+- **Dynamic User Menu**: Shows different options based on auth state
+- **User Information**: Displays current user name/email
+- **Logout Button**: Easy access to logout functionality
+
+## Route Structure
+
+```
+/login - Public login page
+/dashboard - Protected (requires auth)
+/schedule - Protected (requires auth)
+/patients - Protected (requires auth)
+... - All other routes protected
+/** - Catch-all redirects to /login
+```
+
+## Usage Examples
+
+### Adding New Protected Routes
+```typescript
+{ path: 'new-feature', component: NewFeatureComponent, canActivate: [AuthGuard] }
+```
+
+### Checking Auth State in Components
+```typescript
+constructor(private authService: AuthService) {}
+
+ngOnInit() {
+ this.authService.currentUser$.subscribe(user => {
+ if (user) {
+ // User is logged in
+ } else {
+ // User is not logged in
+ }
+ });
+}
+```
+
+### Manual Logout
+```typescript
+this.authService.logout();
+this.router.navigate(['/login']);
+```
+
+## Security Features
+
+- **Route Protection**: All sensitive routes are protected
+- **Persistent Sessions**: Users stay logged in across browser sessions
+- **Automatic Redirects**: Seamless user experience
+- **State Validation**: Authentication state is checked on every route change
+- **Clean Logout**: Complete session cleanup on logout
+
+## Testing
+
+Use these test credentials:
+- **Direct Login**: `user@example.com` / `password123`
+- **MFA Required**: `admin@example.com` / `password123` / `123456`
+
+The system will automatically redirect unauthenticated users to the login page and remember where they were trying to go.
diff --git a/APP/src/app/core/guards/auth.guard.ts b/APP/src/app/core/guards/auth.guard.ts
new file mode 100644
index 0000000..dd00e22
--- /dev/null
+++ b/APP/src/app/core/guards/auth.guard.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@angular/core';
+import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
+import { Observable, of } from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+import { AuthService } from '../../shared/services/auth.service';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthGuard implements CanActivate {
+ constructor(
+ private authService: AuthService,
+ private router: Router
+ ) { }
+
+ canActivate(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+ ): Observable | Promise | boolean {
+ // Check if user is authenticated
+ if (this.authService.isAuthenticated()) {
+ return true;
+ }
+
+ // Store the attempted URL for redirecting after login
+ this.authService.setRedirectUrl(state.url);
+
+ // Redirect to login page
+ this.router.navigate(['/login']);
+ return false;
+ }
+}
diff --git a/APP/src/app/core/interceptors/auth.interceptor.ts b/APP/src/app/core/interceptors/auth.interceptor.ts
new file mode 100644
index 0000000..9fe78cd
--- /dev/null
+++ b/APP/src/app/core/interceptors/auth.interceptor.ts
@@ -0,0 +1,60 @@
+import { HttpInterceptorFn, HttpErrorResponse } from '@angular/common/http';
+import { inject } from '@angular/core';
+import { catchError, throwError } from 'rxjs';
+import { AuthService } from '../../shared/services/auth.service';
+import { Router } from '@angular/router';
+import { ApiConfigService } from '../services/api-config.service';
+
+export const authInterceptor: HttpInterceptorFn = (request, next) => {
+ const authService = inject(AuthService);
+ const apiConfigService = inject(ApiConfigService);
+ const router = inject(Router);
+
+ // Get the current user and token
+ const token = authService.getToken();
+
+ // Clone the request and add the Authorization header if token exists
+ if (token && shouldAddToken(apiConfigService, request)) {
+ request = request.clone({
+ setHeaders: {
+ Authorization: `Bearer ${token}`
+ }
+ });
+ }
+
+ // Handle the request and catch 401 errors
+ return next(request).pipe(
+ catchError((error: HttpErrorResponse) => {
+ if (error.status === 401) {
+ // Token is invalid or expired, logout user
+ authService.logout();
+ router.navigate(['/login']);
+ }
+ return throwError(() => error);
+ })
+ );
+};
+
+/**
+ * Determine if the token should be added to this request
+ * Skip adding token to login requests and other public endpoints
+ */
+function shouldAddToken(apiConfigService: ApiConfigService, request: any): boolean {
+ // Don't add token to outbound requests to other domains
+ if (!request.url.startsWith(apiConfigService.getBaseUrl())) {
+ return false;
+ }
+
+ // Don't add token to login requests
+ if (request.url.includes('/Auth/login') || request.url.includes('/Token/Create')) {
+ return false;
+ }
+ // Don't add token to public endpoints (you can customize this list)
+ const publicEndpoints = [
+ '/Auth/register',
+ '/Auth/forgot-password',
+ '/Auth/reset-password'
+ ];
+
+ return !publicEndpoints.some(endpoint => request.url.includes(endpoint));
+}
diff --git a/APP/src/app/core/services/api-config.service.ts b/APP/src/app/core/services/api-config.service.ts
new file mode 100644
index 0000000..f2cd637
--- /dev/null
+++ b/APP/src/app/core/services/api-config.service.ts
@@ -0,0 +1,58 @@
+import { Injectable } from '@angular/core';
+import { environment } from '../../../environments/environment';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class ApiConfigService {
+ private readonly baseUrl = environment.apiUrl;
+
+ constructor() { }
+
+ /**
+ * Get the full API URL for a specific endpoint
+ * @param endpoint - The API endpoint (e.g., 'Auth', 'Users', 'Transactions')
+ * @returns Full API URL
+ */
+ getApiUrl(endpoint: string): string {
+ return `${this.baseUrl}/${endpoint}`;
+ }
+
+ /**
+ * Get the base API URL
+ * @returns Base API URL
+ */
+ getBaseUrl(): string {
+ return this.baseUrl;
+ }
+
+ /**
+ * Get specific API endpoints
+ */
+ get authUrl(): string {
+ return this.getApiUrl('Auth');
+ }
+
+ get tokenUrl(): string {
+ return this.getApiUrl('Token');
+ }
+
+ get usersUrl(): string {
+ return this.getApiUrl('Users');
+ }
+
+ get transactionsUrl(): string {
+ return this.getApiUrl('Transactions');
+ }
+
+ get dashboardUrl(): string {
+ return this.getApiUrl('Dashboard');
+ }
+ get ordersUrl(): string {
+ return this.getApiUrl('ClientBridgeOrder');
+ }
+
+ get orderDetailUrl(): string {
+ return this.getApiUrl('OrderDetail');
+ }
+}
diff --git a/APP/src/app/core/services/crud-base-api.service.ts b/APP/src/app/core/services/crud-base-api.service.ts
new file mode 100644
index 0000000..9df027f
--- /dev/null
+++ b/APP/src/app/core/services/crud-base-api.service.ts
@@ -0,0 +1,168 @@
+import { HttpClient, HttpErrorResponse, HttpHeaders } from '@angular/common/http';
+import { Inject, Injectable } from '@angular/core';
+import { Observable, throwError } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
+import { ApiConfigService } from './api-config.service';
+
+// Type definitions
+type TextResponse = { message: string };
+/**
+* Base CRUD service that targets the provided controller path.
+*
+* It mirrors the endpoints of CrudBaseApiController:
+* GET /api/{controller}
+* GET /api/{controller}/{id}
+* POST /api/{controller} -> string
+* POST /api/{controller}/batch -> string[]
+* PUT /api/{controller}
+* PUT /api/{controller}/batch -> number
+* DELETE /api/{controller}/{id}
+* DELETE /api/{controller}/batch -> text summary
+* GET /api/{controller}/{id}/exists -> boolean
+* GET /api/{controller}/count -> number
+*/
+@Injectable({ providedIn: 'root' })
+export class CrudBaseApiService {
+ /**
+ * Example: baseUrl = 'https://your-api', controller = 'Customer' →
+ * endpoint = 'https://your-api/api/Customer'
+ */
+ protected readonly endpoint: string;
+
+
+ /**
+ * @param http Angular HttpClient
+ * @param baseUrl API root without trailing slash (e.g., environment.apiBaseUrl)
+ * @param controllerName Controller name (e.g., 'Customer', 'Orders')
+ */
+ constructor(
+ protected http: HttpClient,
+ protected apiConfig: ApiConfigService,
+ @Inject(String) private controllerName: string
+ ) {
+ this.endpoint = apiConfig.getApiUrl(this.controllerName);
+ }
+
+
+ /** Optional default headers (JSON). Override in subclasses if needed. */
+ protected get jsonHeaders(): HttpHeaders {
+ return new HttpHeaders({ 'Content-Type': 'application/json' });
+ }
+
+
+ /** Shared error handler that surfaces useful messages. */
+ protected handleError(error: HttpErrorResponse): Observable {
+ let msg = 'Unknown error';
+ if (error.error instanceof Blob) {
+ // In case backend returns text/plain; charset=utf-8 as Blob
+ return throwError(() => new Error('Server returned an error blob'));
+ }
+ if (typeof error.error === 'string') msg = error.error;
+ else if (error.error?.message) msg = error.error.message;
+ else if (error.message) msg = error.message;
+ return throwError(() => new Error(msg));
+ }
+ /** Prepare the response for the given entity. Override in subclasses if needed. */
+ protected prepareResponse(response: T): T {
+ // Do nothing by default
+ return response;
+ }
+
+ /** GET /api/{controller} */
+ getAll(): Observable {
+ return this.http
+ .get(this.endpoint)
+ .pipe(
+ map(response => {
+
+ for (let i = 0; i < response.length; i++) {
+ const element = response[i];
+ response[i] = this.prepareResponse(element);
+ }
+ return response;
+ }),
+ catchError(err => this.handleError(err)));
+ }
+
+
+ /** GET /api/{controller}/{id} */
+ getById(id: string): Observable {
+ return this.http
+ .get(`${this.endpoint}/${id}`)
+ .pipe(
+ map(response => this.prepareResponse(response)),
+ catchError(err => this.handleError(err)));
+ }
+
+
+
+ /** POST /api/{controller} -> string */
+ create(entity: T): Observable {
+ return this.http
+ .post(this.endpoint, entity, { headers: this.jsonHeaders })
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** POST /api/{controller}/batch -> string[] */
+ createRange(entities: T[]): Observable {
+ return this.http
+ .post(`${this.endpoint}/batch`, entities, { headers: this.jsonHeaders })
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** PUT /api/{controller} */
+ update(entity: T): Observable {
+ return this.http
+ .put(this.endpoint, entity, { headers: this.jsonHeaders })
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** PUT /api/{controller}/batch -> number (updated count) */
+ updateRange(entities: T[]): Observable {
+ return this.http
+ .put(`${this.endpoint}/batch`, entities, { headers: this.jsonHeaders })
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** DELETE /api/{controller}/{id} */
+ delete(id: string): Observable {
+ return this.http
+ .delete(`${this.endpoint}/${id}`)
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** DELETE /api/{controller}/batch -> text summary */
+ deleteRange(ids: string[]): Observable {
+ // API returns a plain text message; map it into a TextResponse for convenience
+ return this.http
+ .delete(`${this.endpoint}/batch`, {
+ body: ids,
+ headers: this.jsonHeaders
+ })
+ .pipe(
+ map((response: any) => ({ message: response || 'Batch delete completed' })),
+ catchError(err => this.handleError(err))
+ );
+ }
+
+
+ /** GET /api/{controller}/{id}/exists -> boolean */
+ exists(id: string): Observable {
+ return this.http
+ .get(`${this.endpoint}/${id}/exists`)
+ .pipe(catchError(err => this.handleError(err)));
+ }
+
+
+ /** GET /api/{controller}/count -> number */
+ count(): Observable {
+ return this.http
+ .get(`${this.endpoint}/count`)
+ .pipe(catchError(err => this.handleError(err)));
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/features/dashboard/dashboard.css b/APP/src/app/features/dashboard/dashboard.css
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/app/features/dashboard/dashboard.html b/APP/src/app/features/dashboard/dashboard.html
new file mode 100644
index 0000000..25d970f
--- /dev/null
+++ b/APP/src/app/features/dashboard/dashboard.html
@@ -0,0 +1,294 @@
+
+ Dashboard
+
+
+
+
+
+
+ {{card.title}}
+ {{card.info}}
+
+
+
+
+
+
+
+
+
+
+
+ Bed
+ Occupancy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Staff
+
+
+
+
+
+
+
+
+
+
+
+
{{dataItem.name}}
+
{{dataItem.specialty}}
+
+
+
+
+
+
+ View all
+
+
+
+
+
+
+
+ Appointments
+
+
+
+
+
+
{{appointment.doctor}}
+
+
+ {{appointment.start}}
+
+
+
+
+
Appointment with {{appointment.patient.name}}.
+
+
+
+ Edit
+ Cancel
+
+
+
+
+ View all appointments
+
+
+
+
+
+
+
+ Infection
+ Rate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Equipment
+ Availability
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Average Length of
+ Stay
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Hospital
+ Visits
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Satisfaction
+ Score
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mortality
+ Rate
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/features/dashboard/dashboard.ts b/APP/src/app/features/dashboard/dashboard.ts
new file mode 100644
index 0000000..da15747
--- /dev/null
+++ b/APP/src/app/features/dashboard/dashboard.ts
@@ -0,0 +1,86 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { ChartsModule, SeriesLabelsContentArgs } from '@progress/kendo-angular-charts';
+import { DateInputsModule } from '@progress/kendo-angular-dateinputs';
+import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
+import { ListViewModule } from '@progress/kendo-angular-listview';
+import { BadgeAlign, IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { IconsModule } from '@progress/kendo-angular-icons';
+import { LayoutModule } from '@progress/kendo-angular-layout';
+import { SVGIcon, envelopeIcon } from '@progress/kendo-svg-icons';
+import {
+ appointments,
+ averageStay,
+ compactCards,
+ departments,
+ donutData,
+ heatmapDataCDC,
+ heatmapDataCampylobacteriosis,
+ heatmapDataHepatitis,
+ heatmapDataInfluenza,
+ heatmapDataMeasles,
+ heatmapDataRSV,
+ hospitalVisits,
+ hours,
+ listItems,
+ mortalityCauses,
+ satisfaction
+} from './models';
+
+@Component({
+ selector: 'app-dashboard',
+ standalone: true,
+ imports: [
+ CommonModule,
+ FormsModule,
+ ChartsModule,
+ DateInputsModule,
+ DropDownsModule,
+ ListViewModule,
+ IndicatorsModule,
+ ButtonsModule,
+ IconsModule,
+ LayoutModule
+ ],
+ templateUrl: './dashboard.html',
+ styleUrl: './dashboard.css'
+})
+export class Dashboard {
+ public cardClasses = 'k-d-flex k-border k-border-solid k-border-border k-bg-surface-alt k-align-items-center k-overflow-x-auto k-p-3 k-gap-6 k-elevation-1 k-rounded-md';
+ public dashboardClasses = 'k-d-flex k-flex-col k-border k-border-solid k-border-border k-bg-surface-alt k-overflow-x-auto k-elevation-1 k-rounded-md';
+
+ public envelopeIcon: SVGIcon = envelopeIcon;
+
+ public badgeAlignBottomEnd: BadgeAlign = {
+ vertical: 'bottom',
+ horizontal: 'end'
+ };
+
+ public chartLabelContent(e: SeriesLabelsContentArgs): string {
+ return e.category;
+ }
+
+ public date = new Date(2023, 5, 14);
+ public date2 = new Date(2023, 5, 15);
+ public averageStay = averageStay;
+ public hours = hours
+ public hospitalVisits = hospitalVisits;
+ public departments = departments;
+ public mortalityCauses = mortalityCauses;
+ public satisfaction = satisfaction;
+ public donutData = donutData;
+ public heatmapDataRSV = heatmapDataRSV;
+ public heatmapDataCDC = heatmapDataCDC;
+ public heatmapDataMeasles = heatmapDataMeasles;
+ public heatmapDataInfluenza = heatmapDataInfluenza;
+ public heatmapDataHepatitis = heatmapDataHepatitis
+ public heatmapDataCampylobacteriosis = heatmapDataCampylobacteriosis;
+ public heatmapData = (dataset: string): any[] => (this as any)[`heatmapData${dataset}`];
+ public appointments = appointments;
+ public ddlData = ['All Departments'];
+ public ddlValue = 'All Departments'
+ public compactCards = compactCards;
+ public listItems: any[] = listItems;
+}
diff --git a/APP/src/app/features/dashboard/models.ts b/APP/src/app/features/dashboard/models.ts
new file mode 100644
index 0000000..b308208
--- /dev/null
+++ b/APP/src/app/features/dashboard/models.ts
@@ -0,0 +1,502 @@
+import { accessibilityIcon, calendarDateIcon, calendarIcon, displayBlockIcon, dollarIcon, fileIcon, inboxIcon, myspaceIcon, pencilIcon, starOutlineIcon } from "@progress/kendo-svg-icons";
+
+export const menuItems = [
+ "Settings",
+ "Support",
+ "Log out"
+];
+
+export const averageStay = [4, 3, 2, 14, 5, 7, 5, 6, 12, 1, 4];
+
+export const hours = Array(48).fill({}).map((_, idx) => `${Math.floor(idx / 2)}:${idx % 2 ? '30': '00'}`);
+
+export const hospitalVisits = [14, 20, 20, 26, 30, 26, 29, 32, 31, 29, 31, 35, 36, 40, 42, 45, 61, 63, 65, 66, 67, 67, 63, 64, 63, 62, 60, 45, 52, 55, 48, 44, 38, 35, 31, 35, 36, 40, 42, 55, 50, 41, 41, 39, 31, 32, 23, 27];
+
+export const departments = [
+ 'Pharmacology & Toxicology',
+ 'Gastroenterology',
+ 'Radiology',
+ 'Orthopedics',
+ 'Outpatient',
+ 'Oncology',
+ 'Neurology',
+ 'ICU',
+ 'Cardiology',
+ 'Emergency',
+ 'Delivery'
+];
+
+export const mortalityCauses = [
+ 'Pharmacology & Toxicology',
+ 'Oncological diseases',
+ 'Circulatory diseases',
+ 'Injury and poisoning',
+ 'Respiratory diseases',
+ 'Endocrine diseases',
+ 'Digestive diseases',
+ 'Nervous system diseases',
+ 'Infectious diseases',
+ 'Kidney diseases',
+ 'Other causes'
+];
+
+export const satisfaction = [
+ {
+ kind: 'Very dissatisfied',
+ share: 60
+ },
+ {
+ kind: 'Dissatisfied',
+ share: 60
+ },
+ {
+ kind: 'Neutral',
+ share: 60
+ },
+ {
+ kind: 'Satisfied',
+ share: 60
+ },
+ {
+ kind: 'Very satisfied',
+ share: 60
+ },
+ {
+ kind: 'Didn\'t answer',
+ share: 60
+}];
+
+export const donutData = [
+ {
+ kind: 'Imaging Equipment',
+ share: 0.17,
+ },
+ {
+ kind: 'Surgical Instruments',
+ share: 0.17,
+ },
+ {
+ kind: 'Electromedical Equipment',
+ share: 0.17,
+ },
+ {
+ kind: 'Transport and Storage',
+ share: 0.17,
+ },
+ {
+ kind: 'Endoscopic Instruments',
+ share: 0.17,
+ },
+ {
+ kind: 'Others',
+ share: 0.17,
+}];
+
+export const heatmapDataRSV = [{
+ a: 'June 2023',
+ b: 'RSV',
+ value: 66
+}, {
+ a: 'May 2023',
+ b: 'RSV',
+ value: 34
+}, {
+ a: 'Apr 2023',
+ b: 'RSV',
+ value: 13
+}, {
+ a: 'Mar 2023',
+ b: 'RSV',
+ value: 49
+}, {
+ a: 'Feb 2023',
+ b: 'RSV',
+ value: 22
+}, {
+ a: 'Jan 2023',
+ b: 'RSV',
+ value: 66
+}, {
+ a: 'Dec 2022',
+ b: 'RSV',
+ value: 78
+}, {
+ a: 'Nov 2022',
+ b: 'RSV',
+ value: 89
+}, {
+ a: 'Oct 2022',
+ b: 'RSV',
+ value: 27
+}, {
+ a: 'Sep 2022',
+ b: 'RSV',
+ value: 83
+}];
+
+export const heatmapDataCDC = [{
+ a: 'June 2023',
+ b: 'CDC',
+ value: 51
+}, {
+ a: 'May 2023',
+ b: 'CDC',
+ value: 84
+}, {
+ a: 'Apr 2023',
+ b: 'CDC',
+ value: 32
+}, {
+ a: 'Mar 2023',
+ b: 'CDC',
+ value: 16
+}, {
+ a: 'Feb 2023',
+ b: 'CDC',
+ value: 11
+}, {
+ a: 'Jan 2023',
+ b: 'CDC',
+ value: 55
+}, {
+ a: 'Dec 2022',
+ b: 'CDC',
+ value: 99
+}, {
+ a: 'Nov 2022',
+ b: 'CDC',
+ value: 42
+}, {
+ a: 'Oct 2022',
+ b: 'CDC',
+ value: 30
+}, {
+ a: 'Sep 2022',
+ b: 'CDC',
+ value: 10
+}];
+
+export const heatmapDataMeasles = [{
+ a: 'June 2023',
+ b: 'Measles',
+ value: 80
+}, {
+ a: 'May 2023',
+ b: 'Measles',
+ value: 56
+}, {
+ a: 'Apr 2023',
+ b: 'Measles',
+ value: 78
+}, {
+ a: 'Mar 2023',
+ b: 'Measles',
+ value: 63
+}, {
+ a: 'Feb 2023',
+ b: 'Measles',
+ value: 24
+}, {
+ a: 'Jan 2023',
+ b: 'Measles',
+ value: 33
+}, {
+ a: 'Dec 2022',
+ b: 'Measles',
+ value: 38
+}, {
+ a: 'Nov 2022',
+ b: 'Measles',
+ value: 17
+}, {
+ a: 'Oct 2022',
+ b: 'Measles',
+ value: 62
+}, {
+ a: 'Sep 2022',
+ b: 'Measles',
+ value: 82
+}];
+
+export const heatmapDataInfluenza = [{
+ a: 'June 2023',
+ b: 'Influenza',
+ value: 84
+}, {
+ a: 'May 2023',
+ b: 'Influenza',
+ value: 25
+}, {
+ a: 'Apr 2023',
+ b: 'Influenza',
+ value: 59
+}, {
+ a: 'Mar 2023',
+ b: 'Influenza',
+ value: 74
+}, {
+ a: 'Feb 2023',
+ b: 'Influenza',
+ value: 41
+}, {
+ a: 'Jan 2023',
+ b: 'Influenza',
+ value: 69
+}, {
+ a: 'Dec 2022',
+ b: 'Influenza',
+ value: 71
+}, {
+ a: 'Nov 2022',
+ b: 'Influenza',
+ value: 11
+}, {
+ a: 'Oct 2022',
+ b: 'Influenza',
+ value: 23
+}, {
+ a: 'Sep 2022',
+ b: 'Influenza',
+ value: 43
+}];
+
+export const heatmapDataHepatitis = [{
+ a: 'June 2023',
+ b: 'Hepatitis',
+ value: 31
+}, {
+ a: 'May 2023',
+ b: 'Hepatitis',
+ value: 27
+}, {
+ a: 'Apr 2023',
+ b: 'Hepatitis',
+ value: 16
+}, {
+ a: 'Mar 2023',
+ b: 'Hepatitis',
+ value: 74
+}, {
+ a: 'Feb 2023',
+ b: 'Hepatitis',
+ value: 50
+}, {
+ a: 'Jan 2023',
+ b: 'Hepatitis',
+ value: 6
+}, {
+ a: 'Dec 2022',
+ b: 'Hepatitis',
+ value: 22
+}, {
+ a: 'Nov 2022',
+ b: 'Hepatitis',
+ value: 65
+}, {
+ a: 'Oct 2022',
+ b: 'Hepatitis',
+ value: 37
+}, {
+ a: 'Sep 2022',
+ b: 'Hepatitis',
+ value: 13
+}];
+
+export const heatmapDataCampylobacteriosis = [{
+ a: 'June 2023',
+ b: 'Campylobacteriosis',
+ value: 66
+}, {
+ a: 'May 2023',
+ b: 'Campylobacteriosis',
+ value: 21
+}, {
+ a: 'Apr 2023',
+ b: 'Campylobacteriosis',
+ value: 52
+}, {
+ a: 'Mar 2023',
+ b: 'Campylobacteriosis',
+ value: 43
+}, {
+ a: 'Feb 2023',
+ b: 'Campylobacteriosis',
+ value: 97
+}, {
+ a: 'Jan 2023',
+ b: 'Campylobacteriosis',
+ value: 81
+}, {
+ a: 'Dec 2022',
+ b: 'Campylobacteriosis',
+ value: 28
+}, {
+ a: 'Nov 2022',
+ b: 'Campylobacteriosis',
+ value: 34
+}, {
+ a: 'Oct 2022',
+ b: 'Campylobacteriosis',
+ value: 45
+}, {
+ a: 'Sep 2022',
+ b: 'Campylobacteriosis',
+ value: 18
+}];
+
+export const appointments = [{
+ doctor: 'Dr. Terrell Fashey',
+ start: '8:30 AM',
+ patient: {
+ name: 'Flora Strosin',
+ phone: '679-747-6105',
+ email: 'flora.strosin@email.com'
+ }
+}, {
+ doctor: 'Dr. Clarence Gulgowski',
+ start: '9:10 AM',
+ patient: {
+ name: 'Michele Nicolas',
+ phone: '884-528-7089',
+ email: 'm.nicolas@email.com'
+ }
+}, {
+ doctor: 'Dr. Jay Mohr',
+ start: '9:45 AM',
+ patient: {
+ name: 'Joseph Pacocha',
+ phone: '777-284-2912',
+ email: 'j.pacocha@email.com'
+ }
+}];
+
+export const compactCards = [{
+ svgIcon: calendarIcon,
+ title: 'Appointments',
+ info: '78 appointments today'
+}, {
+ svgIcon: accessibilityIcon,
+ title: 'Patients',
+ info: '1234 active cases'
+}, {
+ svgIcon: displayBlockIcon,
+ title: 'Beds',
+ info: '56 occupied beds'
+}, {
+ svgIcon: myspaceIcon,
+ title: 'Staff',
+ info: '78 colleagues at work'
+}];
+
+export const listItems = [{
+ name: 'Dr. Teresa Conn',
+ specialty: 'Internal medicine',
+ imageSrc: 'assets/healthcare-dashboard/avatar_1.png'
+}, {
+ name: 'Dr. Mitchell Robel',
+ specialty: 'Pediatrics',
+ imageSrc: 'assets/healthcare-dashboard/avatar_2.png'
+}, {
+ name: 'Dr. Barry Jacobs',
+ specialty: 'Gastroenterology',
+ imageSrc: 'assets/healthcare-dashboard/avatar_3.png'
+}, {
+ name: 'Dr. Nina Bosco',
+ specialty: 'Cardiology',
+ imageSrc: 'assets/healthcare-dashboard/avatar_4.png'
+}];
+
+export const drawerItems = [{
+ text: 'Dashboard',
+ svgIcon: inboxIcon,
+ selected: true,
+ id: 0,
+}, {
+ text: 'Schedule',
+ svgIcon: calendarDateIcon,
+ id: 1
+}, {
+ text: 'Patients',
+ svgIcon: accessibilityIcon,
+ id: 2,
+}, {
+ text: 'Bed Management',
+ svgIcon: displayBlockIcon,
+ id: 3
+}, {
+ text: 'Staff',
+ svgIcon: myspaceIcon,
+ id: 4,
+}, {
+ text: 'Doctors',
+ svgIcon: accessibilityIcon,
+ id: 40,
+ parentId: 4
+}, {
+ text: 'Nurses',
+ svgIcon: accessibilityIcon,
+ id: 41,
+ parentId: 4
+}, {
+ text: 'Therapists',
+ svgIcon: accessibilityIcon,
+ id: 42,
+ parentId: 4
+}, {
+ text: 'Technicians',
+ svgIcon: accessibilityIcon,
+ id: 43,
+ parentId: 4
+}, {
+ text: 'Information technology',
+ svgIcon: accessibilityIcon,
+ id: 44,
+ parentId: 4
+}, {
+ text: 'Food services',
+ svgIcon: accessibilityIcon,
+ id: 45,
+ parentId: 4
+}, {
+ text: 'Environmental services',
+ svgIcon: accessibilityIcon,
+ id: 46,
+ parentId: 4
+}, {
+ text: 'Pharmacy',
+ svgIcon: pencilIcon,
+ id: 5,
+}, {
+ text: 'Reports',
+ svgIcon: fileIcon,
+ id: 6,
+}, {
+ text: 'Report 1',
+ svgIcon: fileIcon,
+ id: 60,
+ parentId: 6
+}, {
+ text: 'Departments',
+ svgIcon: calendarIcon,
+ id: 7,
+}, {
+ text: 'Report 1',
+ svgIcon: calendarIcon,
+ id: 70,
+ parentId: 7
+}, {
+ text: 'Payments',
+ svgIcon: dollarIcon,
+ id: 8,
+}, {
+ text: 'Payments 1',
+ svgIcon: dollarIcon,
+ id: 80,
+ parentId: 8
+}, {
+ separator: true
+}, {
+ text: 'Support',
+ svgIcon: starOutlineIcon,
+ id: 9,
+}];
diff --git a/APP/src/app/features/login-page/login-page.component.html b/APP/src/app/features/login-page/login-page.component.html
new file mode 100644
index 0000000..9647663
--- /dev/null
+++ b/APP/src/app/features/login-page/login-page.component.html
@@ -0,0 +1,238 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
RBJ Identity
+ Escrow Management Portal
+
+
+
+
+
Welcome Back
+
Access your escrow transactions, manage client communications, and track document workflows
+ securely.
+
+
+
+
+
🔒
+
Secure Escrow Management
+
+
+
💬
+
Client Communication
+
+
+
📄
+
Document Management
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Sign In
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Client Access
+
+
+ Admin Access
+
+
+
+
+
+
Client Email:
+
client@example.com
+
+
+
+
+
+
+
+
+
Password:
+
password123
+
+
+
+
+
+
+
+
+
+
+
+
Admin Email:
+
admin@example.com
+
+
+
+
+
+
+
+
+
Password:
+
password123
+
+
+
+
+
+
+
+
+
Security Code:
+
123456
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/features/login-page/login-page.component.scss b/APP/src/app/features/login-page/login-page.component.scss
new file mode 100644
index 0000000..f232d12
--- /dev/null
+++ b/APP/src/app/features/login-page/login-page.component.scss
@@ -0,0 +1,727 @@
+.login-page-container {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ position: relative;
+ overflow: hidden;
+ padding: 1rem;
+}
+
+// Background Shapes
+.background-shapes {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 0;
+}
+
+.shape {
+ position: absolute;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.1);
+ animation: float 6s ease-in-out infinite;
+
+ &.shape-1 {
+ width: 200px;
+ height: 200px;
+ top: 10%;
+ left: 10%;
+ animation-delay: 0s;
+ }
+
+ &.shape-2 {
+ width: 150px;
+ height: 150px;
+ top: 60%;
+ right: 15%;
+ animation-delay: 2s;
+ }
+
+ &.shape-3 {
+ width: 100px;
+ height: 100px;
+ bottom: 20%;
+ left: 20%;
+ animation-delay: 4s;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-20px) rotate(180deg);
+ }
+}
+
+// Main Content
+.login-content {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ max-width: 1200px;
+ width: 100%;
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.15);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ overflow: hidden;
+ position: relative;
+ z-index: 1;
+}
+
+// Branding Section
+.branding-section {
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ padding: 3rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ position: relative;
+
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: url('data:image/svg+xml, ');
+ opacity: 0.3;
+ }
+}
+
+.branding-content {
+ position: relative;
+ z-index: 1;
+ text-align: center;
+}
+
+.logo-container {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 2rem;
+ gap: 1rem;
+
+ .logo-image {
+ height: 60px;
+ width: auto;
+ filter: brightness(0) invert(1);
+ }
+
+ .logo-text {
+ h1 {
+ margin: 0;
+ font-size: 2.5rem;
+ font-weight: 700;
+ letter-spacing: -0.02em;
+ }
+
+ .tagline {
+ font-size: 1rem;
+ opacity: 0.9;
+ font-weight: 300;
+ }
+ }
+}
+
+.welcome-text {
+ margin-bottom: 3rem;
+
+ h2 {
+ font-size: 2rem;
+ font-weight: 600;
+ margin: 0 0 1rem 0;
+ letter-spacing: -0.01em;
+ }
+
+ p {
+ font-size: 1.1rem;
+ opacity: 0.9;
+ line-height: 1.6;
+ margin: 0;
+ }
+}
+
+.features-list {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+
+ .feature-item {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ padding: 0.75rem;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 12px;
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ transition: all 0.3s ease;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.2);
+ transform: translateX(5px);
+ }
+
+ .feature-icon {
+ font-size: 1.5rem;
+ width: 40px;
+ height: 40px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255, 255, 255, 0.2);
+ border-radius: 10px;
+ }
+
+ span {
+ font-weight: 500;
+ font-size: 1rem;
+ }
+ }
+}
+
+// Login Section
+.login-section {
+ padding: 3rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
+.login-card {
+ width: 100%;
+ max-width: 400px;
+}
+
+.login-header {
+ text-align: center;
+ margin-bottom: 2rem;
+
+ h3 {
+ font-size: 2rem;
+ font-weight: 700;
+ color: #1a1a1a;
+ margin: 0 0 0.5rem 0;
+ letter-spacing: -0.01em;
+ }
+
+ p {
+ color: #666;
+ font-size: 1rem;
+ margin: 0;
+ }
+}
+
+.login-actions {
+ margin-bottom: 2rem;
+}
+
+.signin-button {
+ width: 100%;
+ height: 56px;
+ border-radius: 12px;
+ font-size: 1.1rem;
+ font-weight: 600;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border: none;
+ box-shadow: 0 8px 25px rgba(30, 58, 138, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 12px 35px rgba(30, 58, 138, 0.4);
+ }
+
+ &:active {
+ transform: translateY(0);
+ }
+
+ .button-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ }
+
+ .button-icon {
+ width: 20px;
+ height: 20px;
+ }
+}
+
+// Demo Section
+.demo-section {
+ background: #f8f9fa;
+ border-radius: 16px;
+ padding: 1.5rem;
+ border: 1px solid #e9ecef;
+}
+
+.demo-header {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ margin-bottom: 1rem;
+ color: #495057;
+ font-weight: 600;
+ font-size: 0.9rem;
+
+ .demo-icon {
+ width: 16px;
+ height: 16px;
+ }
+}
+
+.credential-tabs {
+ display: flex;
+ background: white;
+ border-radius: 8px;
+ padding: 4px;
+ margin-bottom: 1rem;
+ border: 1px solid #e9ecef;
+}
+
+.tab-button {
+ flex: 1;
+ padding: 0.75rem 1rem;
+ border: none;
+ background: transparent;
+ border-radius: 6px;
+ font-size: 0.9rem;
+ font-weight: 500;
+ color: #6c757d;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &.active {
+ background: #1e40af;
+ color: white;
+ box-shadow: 0 2px 4px rgba(30, 64, 175, 0.2);
+ }
+
+ &:hover:not(.active) {
+ background: #f8f9fa;
+ color: #495057;
+ }
+}
+
+.credential-content {
+ .credential-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem;
+ background: white;
+ border-radius: 8px;
+ margin-bottom: 0.5rem;
+ border: 1px solid #e9ecef;
+ transition: all 0.2s ease;
+
+ &:hover {
+ border-color: #1e40af;
+ box-shadow: 0 2px 8px rgba(30, 64, 175, 0.1);
+ }
+
+ .label {
+ font-weight: 600;
+ color: #495057;
+ min-width: 60px;
+ font-size: 0.9rem;
+ }
+
+ .value {
+ flex: 1;
+ font-family: "Monaco", "Menlo", "Ubuntu Mono", monospace;
+ font-size: 0.9rem;
+ color: #1a1a1a;
+ background: #f8f9fa;
+ padding: 0.25rem 0.5rem;
+ border-radius: 4px;
+ }
+
+ .copy-btn {
+ background: #1e40af;
+ border: none;
+ border-radius: 6px;
+ padding: 0.5rem;
+ color: white;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ background: #1e3a8a;
+ transform: scale(1.05);
+ }
+
+ svg {
+ width: 14px;
+ height: 14px;
+ }
+ }
+ }
+}
+
+// Login Form Styles
+.login-form-state {
+ .login-header {
+ position: relative;
+ text-align: center;
+
+ .back-button {
+ position: absolute;
+ left: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ background: none;
+ border: none;
+ padding: 0.5rem;
+ border-radius: 8px;
+ cursor: pointer;
+ color: #666;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ background: #f8f9fa;
+ color: #1e40af;
+ }
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+
+ h3 {
+ margin: 0 0 0.5rem 0;
+ }
+
+ p {
+ margin: 0 0 1.5rem 0;
+ font-size: 0.95rem;
+ }
+ }
+}
+
+.login-form {
+ .error-message {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: #fee2e2;
+ color: #dc2626;
+ padding: 0.75rem 1rem;
+ border-radius: 8px;
+ border: 1px solid #fecaca;
+ margin-bottom: 1.5rem;
+ font-size: 0.9rem;
+ font-weight: 500;
+
+ .error-icon {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ }
+ }
+
+ .form-field {
+ margin-bottom: 1.5rem;
+
+ kendo-label {
+ display: block;
+ margin-bottom: 0.5rem;
+ font-weight: 600;
+ color: #374151;
+ font-size: 0.9rem;
+ }
+
+ kendo-textbox {
+ width: 100%;
+
+ .k-textbox {
+ height: 48px;
+ border-radius: 8px;
+ border: 2px solid #e5e7eb;
+ font-size: 1rem;
+ transition: all 0.2s ease;
+
+ &:focus {
+ border-color: #1e40af;
+ box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.1);
+ }
+
+ &.k-invalid {
+ border-color: #dc2626;
+ }
+ }
+ }
+
+ .field-error {
+ margin-top: 0.25rem;
+ font-size: 0.8rem;
+ color: #dc2626;
+ font-weight: 500;
+ }
+
+ &.checkbox-field {
+ margin-bottom: 1rem;
+
+ .checkbox-container {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ cursor: pointer;
+ user-select: none;
+
+ kendo-checkbox {
+ margin: 0;
+ }
+
+ .checkbox-label {
+ font-size: 0.9rem;
+ color: #6b7280;
+ font-weight: 500;
+ margin: 0;
+ }
+
+ &:hover .checkbox-label {
+ color: #1e40af;
+ }
+
+ // Style when checkbox is checked
+ &:has(kendo-checkbox:checked) .checkbox-label {
+ color: #1e40af;
+ }
+ }
+ }
+ }
+
+ .form-actions {
+ margin-top: 2rem;
+
+ .submit-button {
+ width: 100%;
+ height: 48px;
+ border-radius: 8px;
+ font-size: 1rem;
+ font-weight: 600;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border: none;
+ box-shadow: 0 4px 12px rgba(30, 58, 138, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover:not(:disabled) {
+ transform: translateY(-1px);
+ box-shadow: 0 6px 20px rgba(30, 58, 138, 0.4);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+ }
+
+ .button-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ }
+
+ .button-icon {
+ width: 18px;
+ height: 18px;
+ }
+ }
+ }
+}
+
+// Mobile Responsive
+@media (max-width: 768px) {
+ .login-page-container {
+ padding: 0.5rem;
+ }
+
+ .login-content {
+ grid-template-columns: 1fr;
+ border-radius: 16px;
+ }
+
+ .branding-section {
+ padding: 2rem 1.5rem;
+ order: 2;
+ }
+
+ .login-section {
+ padding: 2rem 1.5rem;
+ order: 1;
+ }
+
+ .logo-container {
+ flex-direction: column;
+ gap: 0.5rem;
+
+ .logo-text h1 {
+ font-size: 2rem;
+ }
+ }
+
+ .welcome-text {
+ margin-bottom: 2rem;
+
+ h2 {
+ font-size: 1.5rem;
+ }
+
+ p {
+ font-size: 1rem;
+ }
+ }
+
+ .features-list {
+ .feature-item {
+ padding: 0.5rem;
+
+ .feature-icon {
+ width: 35px;
+ height: 35px;
+ font-size: 1.25rem;
+ }
+
+ span {
+ font-size: 0.9rem;
+ }
+ }
+ }
+
+ .login-header h3 {
+ font-size: 1.75rem;
+ }
+
+ .signin-button {
+ height: 50px;
+ font-size: 1rem;
+ }
+
+ .demo-section {
+ padding: 1rem;
+ }
+
+ .credential-tabs {
+ .tab-button {
+ padding: 0.5rem 0.75rem;
+ font-size: 0.85rem;
+ }
+ }
+
+ .credential-content {
+ .credential-item {
+ padding: 0.5rem;
+
+ .label {
+ min-width: 50px;
+ font-size: 0.85rem;
+ }
+
+ .value {
+ font-size: 0.85rem;
+ }
+ }
+ }
+
+ // Login form mobile styles
+ .login-form-state {
+ .login-header {
+ .back-button {
+ padding: 0.4rem;
+
+ svg {
+ width: 18px;
+ height: 18px;
+ }
+ }
+ }
+ }
+
+ .login-form {
+ .form-field {
+ margin-bottom: 1.25rem;
+
+ kendo-textbox .k-textbox {
+ height: 44px;
+ font-size: 0.95rem;
+ }
+ }
+
+ .form-actions .submit-button {
+ height: 44px;
+ font-size: 0.95rem;
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .login-page-container {
+ padding: 0.25rem;
+ }
+
+ .branding-section,
+ .login-section {
+ padding: 1.5rem 1rem;
+ }
+
+ .logo-container .logo-text h1 {
+ font-size: 1.75rem;
+ }
+
+ .welcome-text h2 {
+ font-size: 1.25rem;
+ }
+
+ .features-list {
+ .feature-item {
+ .feature-icon {
+ width: 30px;
+ height: 30px;
+ font-size: 1rem;
+ }
+
+ span {
+ font-size: 0.85rem;
+ }
+ }
+ }
+
+ // Login form extra small mobile styles
+ .login-form {
+ .form-field {
+ margin-bottom: 1rem;
+
+ kendo-textbox .k-textbox {
+ height: 40px;
+ font-size: 0.9rem;
+ }
+ }
+
+ .form-actions .submit-button {
+ height: 40px;
+ font-size: 0.9rem;
+ }
+ }
+}
diff --git a/APP/src/app/features/login-page/login-page.ts b/APP/src/app/features/login-page/login-page.ts
new file mode 100644
index 0000000..3e5ac84
--- /dev/null
+++ b/APP/src/app/features/login-page/login-page.ts
@@ -0,0 +1,205 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { DialogModule, DialogService } from '@progress/kendo-angular-dialog';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { LabelModule } from '@progress/kendo-angular-label';
+import { IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { MfaDialogComponent } from '../../shared/mfa-dialog/mfa-dialog.component';
+import { AuthService, LoginCredentials, LoginResultType, TokenVerificationResult } from '../../shared/services/auth.service';
+import { Router, ActivatedRoute } from '@angular/router';
+
+@Component({
+ selector: 'app-login-page',
+ standalone: true,
+ imports: [
+ CommonModule,
+ DialogModule,
+ ButtonsModule,
+ ReactiveFormsModule,
+ InputsModule,
+ LabelModule,
+ IndicatorsModule,
+ MfaDialogComponent
+ ],
+ templateUrl: './login-page.component.html',
+ styleUrls: ['./login-page.component.scss']
+})
+export class LoginPage implements OnInit {
+ @ViewChild('mfaDialog') mfaDialog!: MfaDialogComponent;
+
+ activeTab: 'user' | 'admin' = 'user';
+ showLoginForm = false;
+ loginForm: FormGroup;
+ isProcessing = false;
+ showError = false;
+ errorMessage = '';
+
+ constructor(
+ private dialogService: DialogService,
+ private authService: AuthService,
+ private router: Router,
+ private route: ActivatedRoute,
+ private fb: FormBuilder
+ ) {
+ this.loginForm = this.fb.group({
+ email: ['', [Validators.required, Validators.email]],
+ password: ['', [Validators.required, Validators.minLength(6)]],
+ rememberMe: [false]
+ });
+ }
+
+ ngOnInit(): void {
+ // Check if user is already logged in
+ if (this.authService.isAuthenticated()) {
+ this.redirectToDashboard();
+ return;
+ }
+
+ // Check for token in URL parameters
+ this.route.queryParams.subscribe(params => {
+ const token = params['token'];
+ if (token) {
+ this.verifySecretLinkToken(token);
+ }
+ });
+ }
+
+ setActiveTab(tab: 'user' | 'admin'): void {
+ this.activeTab = tab;
+ }
+
+ copyToClipboard(text: string): void {
+ navigator.clipboard.writeText(text).then(() => {
+ // You could add a toast notification here
+ console.log('Copied to clipboard:', text);
+ }).catch(err => {
+ console.error('Failed to copy text: ', err);
+ });
+ }
+
+ showLoginFormView(): void {
+ this.showLoginForm = true;
+ // Focus on email input when form appears
+ setTimeout(() => {
+ const emailInput = document.querySelector('input[formControlName="email"]') as HTMLInputElement;
+ if (emailInput) {
+ emailInput.focus();
+ }
+ }, 100);
+ }
+
+ goBackToInitialState(): void {
+ this.showLoginForm = false;
+ this.loginForm.reset();
+ this.showError = false;
+ this.errorMessage = '';
+ }
+
+ onSubmit(): void {
+ if (this.loginForm.valid && !this.isProcessing) {
+ this.isProcessing = true;
+ this.showError = false;
+
+ const credentials: LoginCredentials = this.loginForm.value;
+
+ this.authService.login(credentials).subscribe({
+ next: (result) => {
+ this.isProcessing = false;
+
+ if (result.result === LoginResultType.Success) {
+ this.authService.setCurrentUser(result.responseData!);
+ this.redirectToDashboard();
+ } else if (result.result === LoginResultType.MfaRequired) {
+ this.showMfaDialog(credentials);
+ } else {
+ this.showError = true;
+ this.errorMessage = result.message || 'Invalid email or password';
+ }
+ },
+ error: (error) => {
+ this.isProcessing = false;
+ this.showError = true;
+ this.errorMessage = 'An error occurred during login. Please try again.';
+ console.error('Login error:', error);
+ }
+ });
+ }
+ }
+
+ get emailControl() {
+ return this.loginForm.get('email');
+ }
+
+ get passwordControl() {
+ return this.loginForm.get('password');
+ }
+
+ private showMfaDialog(credentials: LoginCredentials): void {
+ if (this.mfaDialog) {
+ // Set the login data for MFA dialog
+ (this.mfaDialog as any).loginData = credentials;
+
+ // Show MFA dialog
+ this.mfaDialog.show();
+ }
+ }
+
+ onMfaSuccess(userData: any): void {
+ this.authService.setCurrentUser(userData);
+ this.redirectToDashboard();
+ }
+
+ onMfaCancel(): void {
+ // Reset form and focus on email
+ this.loginForm.reset();
+ setTimeout(() => {
+ const emailInput = document.querySelector('input[formControlName="email"]') as HTMLInputElement;
+ if (emailInput) {
+ emailInput.focus();
+ }
+ }, 100);
+ }
+
+
+ private verifySecretLinkToken(token: string): void {
+ this.isProcessing = true;
+ this.showError = false;
+
+ // First check if token is expired locally
+ if (this.authService.isTokenExpired(token)) {
+ this.isProcessing = false;
+ this.showError = true;
+ this.errorMessage = 'This link has expired. Please request a new one.';
+ return;
+ }
+
+ this.authService.verifySecretLinkToken(token).subscribe({
+ next: (result: TokenVerificationResult) => {
+ this.isProcessing = false;
+
+ if (result.isValid && result.user) {
+ // Token is valid, set user and redirect
+ this.authService.setCurrentUser(result.user);
+ this.redirectToDashboard();
+ } else {
+ // Token verification failed
+ this.showError = true;
+ this.errorMessage = result.message || 'Invalid or expired link. Please request a new one.';
+ }
+ },
+ error: (error) => {
+ this.isProcessing = false;
+ this.showError = true;
+ this.errorMessage = 'An error occurred while verifying the link. Please try again.';
+ console.error('Token verification error:', error);
+ }
+ });
+ }
+
+ private redirectToDashboard(): void {
+ const redirectUrl = this.authService.getRedirectUrl();
+ this.router.navigate([redirectUrl || '/dashboard']);
+ }
+}
diff --git a/APP/src/app/features/schedule/schedule.css b/APP/src/app/features/schedule/schedule.css
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/app/layout/footer/footer.component.html b/APP/src/app/layout/footer/footer.component.html
new file mode 100644
index 0000000..c387d55
--- /dev/null
+++ b/APP/src/app/layout/footer/footer.component.html
@@ -0,0 +1,5 @@
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/layout/footer/footer.component.scss b/APP/src/app/layout/footer/footer.component.scss
new file mode 100644
index 0000000..9922668
--- /dev/null
+++ b/APP/src/app/layout/footer/footer.component.scss
@@ -0,0 +1,3 @@
+footer {
+ margin-top: auto;
+}
diff --git a/APP/src/app/layout/footer/footer.component.ts b/APP/src/app/layout/footer/footer.component.ts
new file mode 100644
index 0000000..deac2c2
--- /dev/null
+++ b/APP/src/app/layout/footer/footer.component.ts
@@ -0,0 +1,14 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'app-footer',
+ standalone: true,
+ imports: [CommonModule],
+ templateUrl: './footer.component.html',
+ styleUrls: ['./footer.component.scss']
+})
+export class FooterComponent {
+ public currentYear = new Date().getFullYear();
+}
+
diff --git a/APP/src/app/layout/header/header.component.html b/APP/src/app/layout/header/header.component.html
new file mode 100644
index 0000000..2a67fa3
--- /dev/null
+++ b/APP/src/app/layout/header/header.component.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+ RBJ Identity Portal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isAuthenticated ? (getDisplayName() || currentUser?.email || 'User') : 'Sign In' }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/layout/header/header.component.scss b/APP/src/app/layout/header/header.component.scss
new file mode 100644
index 0000000..66836bc
--- /dev/null
+++ b/APP/src/app/layout/header/header.component.scss
@@ -0,0 +1,48 @@
+/* Logo styling */
+.logo-link {
+ //display: flex;
+ align-items: center;
+ gap: 12px;
+ text-decoration: none;
+ transition: opacity 0.2s ease;
+
+ &:hover {
+ opacity: 0.85;
+ }
+}
+
+.logo-text {
+ font-size: 1.25rem;
+ font-weight: 600;
+ letter-spacing: -0.02em;
+ background: linear-gradient(135deg, #0066cc 0%, #0052a3 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+ background-clip: text;
+ white-space: nowrap;
+
+ // Fallback for browsers that don't support background-clip
+ @supports not ((-webkit-background-clip: text) or (background-clip: text)) {
+ color: #0066cc;
+ background: none;
+ -webkit-text-fill-color: initial;
+ }
+}
+
+/* Search box responsive styling */
+.search-box-wrapper {
+ width: 100%;
+ max-width: 360px;
+}
+
+.search-box {
+ width: 100%;
+}
+
+/* Mobile optimizations */
+@media (max-width: 767px) {
+ /* Optimize spacing on mobile */
+ kendo-appbar {
+ padding: 0 8px;
+ }
+}
diff --git a/APP/src/app/layout/header/header.component.ts b/APP/src/app/layout/header/header.component.ts
new file mode 100644
index 0000000..3e845d3
--- /dev/null
+++ b/APP/src/app/layout/header/header.component.ts
@@ -0,0 +1,128 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { AppBarModule } from '@progress/kendo-angular-navigation';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
+import { IconsModule } from '@progress/kendo-angular-icons';
+import { SVGIcon, bellIcon, menuIcon, searchIcon, userIcon, logoutIcon } from '@progress/kendo-svg-icons';
+import { LayoutService } from '../services/layout.service';
+import { AuthService, User } from '../../shared/services/auth.service';
+import { Subject, takeUntil } from 'rxjs';
+
+@Component({
+ selector: 'app-header',
+ standalone: true,
+ imports: [
+ CommonModule,
+ AppBarModule,
+ ButtonsModule,
+ IndicatorsModule,
+ InputsModule,
+ IconsModule,
+ DropDownsModule
+ ],
+ templateUrl: './header.component.html',
+ styleUrls: ['./header.component.scss']
+})
+export class HeaderComponent implements OnInit, OnDestroy {
+ public menuIcon: SVGIcon = menuIcon;
+ public searchIcon: SVGIcon = searchIcon;
+ public bellIcon: SVGIcon = bellIcon;
+ public userIcon: SVGIcon = userIcon;
+ public logoutIcon: SVGIcon = logoutIcon;
+
+ public userMenuItems: any[] = [];
+ public currentUser: User | null = null;
+ public isAuthenticated = false;
+
+ public badgeAlign = {
+ vertical: 'top' as const,
+ horizontal: 'end' as const
+ };
+
+ private destroy$ = new Subject();
+
+ constructor(
+ public layoutService: LayoutService,
+ private authService: AuthService,
+ private router: Router
+ ) { }
+
+ ngOnInit(): void {
+ // Subscribe to authentication state changes
+ this.authService.currentUser$
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(user => {
+ this.currentUser = user;
+ this.isAuthenticated = !!user;
+ this.updateUserMenu();
+ });
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ public onMenuClick(): void {
+ this.layoutService.toggleDrawer();
+ }
+
+ public onLogout(): void {
+ this.authService.logout();
+ this.router.navigate(['/login']);
+ }
+
+ public onUserMenuClick(item: any): void {
+ if (item.click) {
+ item.click();
+ }
+ }
+
+ public getDisplayName(): string {
+ if (this.currentUser) {
+ const fullName = `${this.currentUser.firstName} ${this.currentUser.lastName}`.trim();
+ return fullName || this.currentUser.email;
+ }
+ return '';
+ }
+
+ private updateUserMenu(): void {
+ if (this.isAuthenticated && this.currentUser) {
+ this.userMenuItems = [
+ {
+ text: `Welcome, ${this.getDisplayName() || this.currentUser.email}`,
+ disabled: true
+ },
+ { separator: true },
+ {
+ text: 'Profile',
+ icon: 'user',
+ disabled: true
+ },
+ {
+ text: 'Settings',
+ icon: 'settings',
+ disabled: true
+ },
+ { separator: true },
+ {
+ text: 'Logout',
+ icon: 'logout',
+ click: () => this.onLogout()
+ }
+ ];
+ } else {
+ this.userMenuItems = [
+ {
+ text: 'Sign In',
+ click: () => this.router.navigate(['/login'])
+ }
+ ];
+ }
+ }
+}
+
diff --git a/APP/src/app/layout/navbar/navbar.component.html b/APP/src/app/layout/navbar/navbar.component.html
new file mode 100644
index 0000000..ae2858b
--- /dev/null
+++ b/APP/src/app/layout/navbar/navbar.component.html
@@ -0,0 +1,23 @@
+
+
+
+ @if (item.svgIcon) {
+
+ }
+ {{item.text}}
+ @if (hasChildren) {
+
+
+
+
+ }
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/layout/navbar/navbar.component.scss b/APP/src/app/layout/navbar/navbar.component.scss
new file mode 100644
index 0000000..6e80432
--- /dev/null
+++ b/APP/src/app/layout/navbar/navbar.component.scss
@@ -0,0 +1,20 @@
+/* Drawer animation */
+kendo-drawer {
+ transition: transform 0.3s ease-in-out;
+}
+
+/* Mobile optimizations */
+@media (max-width: 767px) {
+ /* Ensure drawer overlay has proper z-index */
+ kendo-drawer.k-drawer-overlay {
+ z-index: 9999;
+ }
+}
+
+/* Tablet optimizations */
+@media (min-width: 768px) and (max-width: 1023px) {
+ /* Adjust drawer width for tablets if needed */
+ kendo-drawer {
+ max-width: 280px;
+ }
+}
diff --git a/APP/src/app/layout/navbar/navbar.component.ts b/APP/src/app/layout/navbar/navbar.component.ts
new file mode 100644
index 0000000..5408c97
--- /dev/null
+++ b/APP/src/app/layout/navbar/navbar.component.ts
@@ -0,0 +1,77 @@
+import { Component } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, RouterOutlet } from '@angular/router';
+import { LayoutModule } from '@progress/kendo-angular-layout';
+import { IconsModule } from '@progress/kendo-angular-icons';
+import { SVGIcon, chevronDownIcon, chevronUpIcon } from '@progress/kendo-svg-icons';
+import { DrawerItemExpandedFn, DrawerSelectEvent } from '@progress/kendo-angular-layout';
+import { LayoutService } from '../services/layout.service';
+import { drawerItems } from '../../features/dashboard/models';
+import { FooterComponent } from '../footer/footer.component';
+
+@Component({
+ selector: 'app-navbar',
+ standalone: true,
+ imports: [
+ CommonModule,
+ RouterOutlet,
+ LayoutModule,
+ IconsModule,
+ FooterComponent
+ ],
+ templateUrl: './navbar.component.html',
+ styleUrls: ['./navbar.component.scss']
+})
+export class NavbarComponent {
+ public chevronUpIcon: SVGIcon = chevronUpIcon;
+ public chevronDownIcon: SVGIcon = chevronDownIcon;
+
+ public drawerItems = drawerItems;
+ public selectedDrawerItem = 'Dashboard';
+ public expandedItems: Array = [4];
+
+ constructor(
+ private router: Router,
+ public layoutService: LayoutService
+ ) { }
+
+ public onSelect(ev: DrawerSelectEvent): void {
+ this.selectedDrawerItem = ev.item.text;
+ const current = ev.item.id;
+
+ if (this.expandedItems.indexOf(current) >= 0) {
+ this.expandedItems = this.expandedItems.filter((id) => id !== current);
+ } else {
+ this.expandedItems.push(current);
+ }
+
+ // Auto-collapse drawer on mobile after selection
+ if (this.layoutService.isMobile()) {
+ this.layoutService.closeDrawer();
+ }
+
+ // Navigate based on the selected item
+ const routeMap: { [key: string]: string } = {
+ 'Dashboard': '/dashboard',
+ 'Schedule': '/schedule',
+ 'Patients': '/patients',
+ 'Bed Management': '/bed-management',
+ 'Staff': '/staff',
+ 'Pharmacy': '/pharmacy',
+ 'Reports': '/reports',
+ 'Departments': '/departments',
+ 'Payments': '/payments',
+ 'Support': '/support'
+ };
+
+ const route = routeMap[ev.item.text];
+ if (route) {
+ this.router.navigate([route]);
+ }
+ }
+
+ public isItemExpanded: DrawerItemExpandedFn = (item): boolean => {
+ return this.expandedItems.indexOf(item.id) >= 0;
+ };
+}
+
diff --git a/APP/src/app/layout/services/layout.service.ts b/APP/src/app/layout/services/layout.service.ts
new file mode 100644
index 0000000..5c3fcd9
--- /dev/null
+++ b/APP/src/app/layout/services/layout.service.ts
@@ -0,0 +1,84 @@
+import { Injectable, signal, computed } from '@angular/core';
+import { DrawerMode } from '@progress/kendo-angular-layout';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class LayoutService {
+ // Signals for reactive state management
+ private readonly windowWidth = signal(typeof window !== 'undefined' ? window.innerWidth : 1024);
+
+ // Computed signals for responsive breakpoints
+ public readonly isMobile = computed(() => this.windowWidth() < 768);
+ public readonly isTablet = computed(() => this.windowWidth() >= 768 && this.windowWidth() < 1024);
+ public readonly isDesktop = computed(() => this.windowWidth() >= 1024);
+
+ // Drawer state
+ public readonly drawerExpanded = signal(true);
+ public readonly drawerMode = computed(() =>
+ this.isMobile() ? 'overlay' : 'push'
+ );
+ public readonly drawerAutoCollapse = computed(() => this.isMobile());
+
+ constructor() {
+ this.initializeResizeListener();
+ this.updateDrawerState();
+ }
+
+ /**
+ * Initialize window resize listener
+ */
+ private initializeResizeListener(): void {
+ if (typeof window !== 'undefined') {
+ window.addEventListener('resize', () => this.handleResize());
+ }
+ }
+
+ /**
+ * Handle window resize events
+ */
+ private handleResize(): void {
+ this.windowWidth.set(window.innerWidth);
+ this.updateDrawerState();
+ }
+
+ /**
+ * Update drawer state based on screen size
+ */
+ private updateDrawerState(): void {
+ if (this.isMobile()) {
+ this.drawerExpanded.set(false);
+ } else {
+ this.drawerExpanded.set(true);
+ }
+ }
+
+ /**
+ * Toggle drawer open/closed state
+ */
+ public toggleDrawer(): void {
+ this.drawerExpanded.update(expanded => !expanded);
+ }
+
+ /**
+ * Close drawer (useful for mobile after navigation)
+ */
+ public closeDrawer(): void {
+ this.drawerExpanded.set(false);
+ }
+
+ /**
+ * Open drawer
+ */
+ public openDrawer(): void {
+ this.drawerExpanded.set(true);
+ }
+
+ /**
+ * Get current window width
+ */
+ public getWindowWidth(): number {
+ return this.windowWidth();
+ }
+}
+
diff --git a/APP/src/app/portals/user-portal/components/user-header/user-header.component.html b/APP/src/app/portals/user-portal/components/user-header/user-header.component.html
new file mode 100644
index 0000000..026598b
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-header/user-header.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ RBJ Identity Portal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ getDisplayName() || currentUser?.email || 'User' }}
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/portals/user-portal/components/user-header/user-header.component.scss b/APP/src/app/portals/user-portal/components/user-header/user-header.component.scss
new file mode 100644
index 0000000..7b4176f
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-header/user-header.component.scss
@@ -0,0 +1,21 @@
+.logo-link {
+ display: flex;
+ align-items: center;
+ text-decoration: none;
+ color: inherit;
+
+ .logo-text {
+ margin-left: 0.5rem;
+ font-weight: 600;
+ font-size: 1.1rem;
+ }
+}
+
+.search-box-wrapper {
+ width: 100%;
+ max-width: 400px;
+
+ .search-box {
+ width: 100%;
+ }
+}
diff --git a/APP/src/app/portals/user-portal/components/user-header/user-header.component.ts b/APP/src/app/portals/user-portal/components/user-header/user-header.component.ts
new file mode 100644
index 0000000..a203c42
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-header/user-header.component.ts
@@ -0,0 +1,118 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router } from '@angular/router';
+import { AppBarModule } from '@progress/kendo-angular-navigation';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { DropDownsModule } from '@progress/kendo-angular-dropdowns';
+import { IconsModule } from '@progress/kendo-angular-icons';
+import { SVGIcon, bellIcon, menuIcon, searchIcon, userIcon, logoutIcon } from '@progress/kendo-svg-icons';
+import { AuthService, User } from '../../../../shared/services/auth.service';
+import { LayoutService } from '../../../../layout/services/layout.service';
+import { Subject, takeUntil } from 'rxjs';
+
+@Component({
+ selector: 'app-user-header',
+ standalone: true,
+ imports: [
+ CommonModule,
+ AppBarModule,
+ ButtonsModule,
+ IndicatorsModule,
+ InputsModule,
+ IconsModule,
+ DropDownsModule
+ ],
+ templateUrl: './user-header.component.html',
+ styleUrls: ['./user-header.component.scss']
+})
+export class UserHeaderComponent implements OnInit, OnDestroy {
+ public menuIcon: SVGIcon = menuIcon;
+ public searchIcon: SVGIcon = searchIcon;
+ public bellIcon: SVGIcon = bellIcon;
+ public userIcon: SVGIcon = userIcon;
+ public logoutIcon: SVGIcon = logoutIcon;
+
+ public userMenuItems: any[] = [];
+ public currentUser: User | null = null;
+
+ public badgeAlign = {
+ vertical: 'top' as const,
+ horizontal: 'end' as const
+ };
+
+ private destroy$ = new Subject();
+
+ constructor(
+ private authService: AuthService,
+ private layoutService: LayoutService,
+ private router: Router
+ ) { }
+
+ ngOnInit(): void {
+ // Subscribe to authentication state changes
+ this.authService.currentUser$
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(user => {
+ this.currentUser = user;
+ this.updateUserMenu();
+ });
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ public onMenuClick(): void {
+ this.layoutService.toggleDrawer();
+ }
+
+ public onLogout(): void {
+ this.authService.logout();
+ this.router.navigate(['/login']);
+ }
+
+ public onUserMenuClick(item: any): void {
+ if (item.click) {
+ item.click();
+ }
+ }
+
+ public getDisplayName(): string {
+ if (this.currentUser) {
+ const fullName = `${this.currentUser.firstName} ${this.currentUser.lastName}`.trim();
+ return fullName || this.currentUser.email;
+ }
+ return '';
+ }
+
+ private updateUserMenu(): void {
+ if (this.currentUser) {
+ this.userMenuItems = [
+ {
+ text: `Welcome, ${this.getDisplayName() || this.currentUser.email}`,
+ disabled: true
+ },
+ { separator: true },
+ {
+ text: 'Profile',
+ icon: 'user',
+ disabled: true
+ },
+ {
+ text: 'Settings',
+ icon: 'settings',
+ disabled: true
+ },
+ { separator: true },
+ {
+ text: 'Logout',
+ icon: 'logout',
+ click: () => this.onLogout()
+ }
+ ];
+ }
+ }
+}
diff --git a/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.html b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.html
new file mode 100644
index 0000000..7fb8370
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
Main
+
+ {{ item.text }}
+
+
+
+
+
Management
+
+ {{ item.text }}
+
+
+
+
+
Support
+
+ {{ item.text }}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.scss b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.scss
new file mode 100644
index 0000000..1e84f9f
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.scss
@@ -0,0 +1,66 @@
+.drawer-content {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+.drawer-header {
+ padding: 1.5rem 1rem;
+ border-bottom: 1px solid #e0e0e0;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+
+ h3 {
+ margin: 0 0 0.25rem 0;
+ font-size: 1.25rem;
+ font-weight: 600;
+ }
+
+ p {
+ margin: 0;
+ font-size: 0.875rem;
+ opacity: 0.9;
+ }
+}
+
+.drawer-nav {
+ flex: 1;
+ padding: 1rem 0;
+ overflow-y: auto;
+}
+
+.nav-section {
+ margin-bottom: 1.5rem;
+
+ h4 {
+ margin: 0 0 0.5rem 0;
+ padding: 0 1rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: #666;
+ }
+}
+
+.nav-button {
+ width: 100%;
+ justify-content: flex-start;
+ text-align: left;
+ padding: 0.75rem 1rem;
+ margin: 0.125rem 0;
+ border-radius: 0;
+
+ &:hover {
+ background-color: #f8f9fa;
+ }
+
+ &.k-button-solid {
+ background-color: #e3f2fd;
+ color: #1976d2;
+
+ &:hover {
+ background-color: #bbdefb;
+ }
+ }
+}
diff --git a/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.ts b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.ts
new file mode 100644
index 0000000..dc92aab
--- /dev/null
+++ b/APP/src/app/portals/user-portal/components/user-navbar/user-navbar.component.ts
@@ -0,0 +1,106 @@
+import { Component, OnInit, OnDestroy } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, NavigationEnd } from '@angular/router';
+import { LayoutModule } from '@progress/kendo-angular-layout';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { IconsModule } from '@progress/kendo-angular-icons';
+import { SVGIcon, homeIcon, calendarIcon, userIcon } from '@progress/kendo-svg-icons';
+import { LayoutService } from '../../../../layout/services/layout.service';
+import { Subject, takeUntil, filter } from 'rxjs';
+
+interface NavItem {
+ text: string;
+ icon: SVGIcon;
+ path: string;
+ active?: boolean;
+}
+
+@Component({
+ selector: 'app-user-navbar',
+ standalone: true,
+ imports: [
+ CommonModule,
+ LayoutModule,
+ ButtonsModule,
+ IconsModule
+ ],
+ templateUrl: './user-navbar.component.html',
+ styleUrls: ['./user-navbar.component.scss']
+})
+export class UserNavbarComponent implements OnInit, OnDestroy {
+ public homeIcon: SVGIcon = homeIcon;
+ public calendarIcon: SVGIcon = calendarIcon;
+ public peopleIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public bedIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public userIcon: SVGIcon = userIcon;
+ public pillIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public chartIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public buildingIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public creditCardIcon: SVGIcon = userIcon; // Using userIcon as fallback
+ public supportIcon: SVGIcon = userIcon; // Using userIcon as fallback
+
+ public mainNavItems: NavItem[] = [
+ { text: 'Dashboard', icon: this.homeIcon, path: '/user-portal/dashboard' },
+ { text: 'Schedule', icon: this.calendarIcon, path: '/user-portal/schedule' },
+ { text: 'Patients', icon: this.peopleIcon, path: '/user-portal/patients' }
+ ];
+
+ public managementNavItems: NavItem[] = [
+ { text: 'Bed Management', icon: this.bedIcon, path: '/user-portal/bed-management' },
+ { text: 'Staff', icon: this.userIcon, path: '/user-portal/staff' },
+ { text: 'Pharmacy', icon: this.pillIcon, path: '/user-portal/pharmacy' },
+ { text: 'Reports', icon: this.chartIcon, path: '/user-portal/reports' },
+ { text: 'Departments', icon: this.buildingIcon, path: '/user-portal/departments' },
+ { text: 'Payments', icon: this.creditCardIcon, path: '/user-portal/payments' }
+ ];
+
+ public supportNavItems: NavItem[] = [
+ { text: 'Support', icon: this.supportIcon, path: '/user-portal/support' }
+ ];
+
+ private destroy$ = new Subject();
+
+ constructor(
+ public layoutService: LayoutService,
+ private router: Router
+ ) { }
+
+ ngOnInit(): void {
+ // Listen to route changes to update active states
+ this.router.events
+ .pipe(
+ filter(event => event instanceof NavigationEnd),
+ takeUntil(this.destroy$)
+ )
+ .subscribe((event: NavigationEnd) => {
+ this.updateActiveStates(event.url);
+ });
+
+ // Set initial active state
+ this.updateActiveStates(this.router.url);
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ public navigateTo(path: string): void {
+ this.router.navigate([path]);
+ this.layoutService.closeDrawer();
+ }
+
+ private updateActiveStates(currentUrl: string): void {
+ // Reset all active states
+ [...this.mainNavItems, ...this.managementNavItems, ...this.supportNavItems]
+ .forEach(item => item.active = false);
+
+ // Set active state for current route
+ const activeItem = [...this.mainNavItems, ...this.managementNavItems, ...this.supportNavItems]
+ .find(item => currentUrl.startsWith(item.path));
+
+ if (activeItem) {
+ activeItem.active = true;
+ }
+ }
+}
diff --git a/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.html b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.html
new file mode 100644
index 0000000..a46cfe5
--- /dev/null
+++ b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.html
@@ -0,0 +1,172 @@
+
+
+
+
+
Welcome back, {{ getDisplayName() || 'User' }}!
+
Here's a mock overview of the RBJ Identity escrow dashboard.
+
+
+
+
+
+
+
+
+
{{ activeTransactions }}
+
Active Transactions
+
+
+
+
+
+
+
{{ pendingTasks }}
+
Pending Tasks
+
+
+
+
+
+
+
{{ completedTransactions }}
+
Completed
+
+
+
+
+
+
+
${{ totalValue | number:'1.0-0' }}
+
Total Value
+
+
+
+
+
+
+
+
+
+
+
0">
+
+
+
+
${{ transaction.amount | number:'1.0-0' }}
+
{{ transaction.date | date:'MMM d, y' }}
+
+
+
+
{{ transaction.progress }}% Complete
+
+
+
+
+
+
+
+
No Recent Transactions
+
You don't have any recent transactions yet.
+
+
+
+
+
+
+
+
+
+
+
+
+
New Transaction
+
Start a new escrow process
+
+
+
+
+
+
+
Manage Tasks
+
View and update your tasks
+
+
+
+
+
+
+
Contacts
+
Manage your contacts
+
+
+
+
+
+
+
Messages
+
Check your messages
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.scss b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.scss
new file mode 100644
index 0000000..eb7af1d
--- /dev/null
+++ b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.scss
@@ -0,0 +1,536 @@
+.dashboard-container {
+ max-width: 1200px;
+ margin: 0 auto;
+}
+
+// Welcome Section
+.welcome-section {
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border-radius: 16px;
+ padding: 2rem;
+ margin-bottom: 2rem;
+ color: white;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ position: relative;
+ overflow: hidden;
+
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 200px;
+ height: 200px;
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 50%;
+ transform: translate(50%, -50%);
+ }
+
+ .welcome-content {
+ position: relative;
+ z-index: 1;
+
+ h1 {
+ margin: 0 0 0.5rem 0;
+ font-size: 2rem;
+ font-weight: 700;
+ letter-spacing: -0.01em;
+ }
+
+ p {
+ margin: 0;
+ font-size: 1.1rem;
+ opacity: 0.9;
+ }
+ }
+
+ .welcome-actions {
+ position: relative;
+ z-index: 1;
+ }
+
+ .action-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1.5rem;
+ background: rgba(255, 255, 255, 0.2);
+ border: 1px solid rgba(255, 255, 255, 0.3);
+ border-radius: 8px;
+ color: white;
+ text-decoration: none;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ cursor: pointer;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.3);
+ transform: translateY(-2px);
+ }
+
+ svg {
+ width: 18px;
+ height: 18px;
+ }
+ }
+}
+
+// Stats Grid
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ gap: 1.5rem;
+ margin-bottom: 2rem;
+}
+
+.stat-card {
+ background: white;
+ border-radius: 12px;
+ padding: 1.5rem;
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
+ border: 1px solid #e5e7eb;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ transition: all 0.3s ease;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+ }
+
+ .stat-icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+
+ &.active {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+
+ &.pending {
+ background: rgba(251, 191, 36, 0.1);
+ color: #d97706;
+ }
+
+ &.completed {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ }
+
+ &.total {
+ background: rgba(168, 85, 247, 0.1);
+ color: #9333ea;
+ }
+
+ svg {
+ width: 24px;
+ height: 24px;
+ }
+ }
+
+ .stat-content {
+ flex: 1;
+
+ .stat-value {
+ font-size: 1.75rem;
+ font-weight: 700;
+ color: #1f2937;
+ margin-bottom: 0.25rem;
+ }
+
+ .stat-label {
+ font-size: 0.9rem;
+ color: #6b7280;
+ font-weight: 500;
+ }
+ }
+}
+
+// Section Styles
+.section {
+ margin-bottom: 2rem;
+
+ .section-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1.5rem;
+
+ h2 {
+ margin: 0;
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: #1f2937;
+ }
+
+ .view-all-btn {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ background: none;
+ border: none;
+ color: #1e40af;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+
+ &:hover {
+ color: #1e3a8a;
+ }
+
+ svg {
+ width: 16px;
+ height: 16px;
+ }
+ }
+ }
+}
+
+// Transactions List
+.transactions-list {
+ display: grid;
+ gap: 1rem;
+}
+
+// Loading State
+.loading-container {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 3rem 1rem;
+ text-align: center;
+
+ .loading-spinner {
+ width: 40px;
+ height: 40px;
+ border: 3px solid #e5e7eb;
+ border-top: 3px solid #1e40af;
+ border-radius: 50%;
+ animation: spin 1s linear infinite;
+ margin-bottom: 1rem;
+ }
+
+ .loading-text {
+ color: #6b7280;
+ font-size: 0.9rem;
+ margin: 0;
+ }
+}
+
+@keyframes spin {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+// Empty State
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 3rem 1rem;
+ text-align: center;
+
+ .empty-icon {
+ width: 64px;
+ height: 64px;
+ background: rgba(30, 64, 175, 0.1);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #1e40af;
+ margin-bottom: 1.5rem;
+
+ svg {
+ width: 32px;
+ height: 32px;
+ }
+ }
+
+ h3 {
+ margin: 0 0 0.5rem 0;
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: #1f2937;
+ }
+
+ p {
+ margin: 0 0 1.5rem 0;
+ color: #6b7280;
+ font-size: 0.9rem;
+ }
+
+ .action-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem 1.5rem;
+ background: #1e40af;
+ border: none;
+ border-radius: 8px;
+ color: white;
+ text-decoration: none;
+ font-weight: 600;
+ transition: all 0.3s ease;
+ cursor: pointer;
+
+ &:hover {
+ background: #1e3a8a;
+ transform: translateY(-2px);
+ }
+ }
+}
+
+.transaction-card {
+ background: white;
+ border-radius: 12px;
+ padding: 1.5rem;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ border: 1px solid #e5e7eb;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ margin-bottom: 10px;
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+ border-color: #1e40af;
+ }
+
+ .transaction-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+
+ .transaction-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: #1f2937;
+ }
+
+ .transaction-status {
+ padding: 0.25rem 0.75rem;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ text-transform: uppercase;
+
+ &.active {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+
+ &.pending {
+ background: rgba(251, 191, 36, 0.1);
+ color: #d97706;
+ }
+
+ &.completed {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ }
+ }
+ }
+
+ .transaction-details {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 1rem;
+
+ .transaction-amount {
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: #1f2937;
+ }
+
+ .transaction-date {
+ font-size: 0.9rem;
+ color: #6b7280;
+ }
+ }
+
+ .transaction-progress {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+
+ .progress-bar {
+ flex: 1;
+ height: 6px;
+ background: #e5e7eb;
+ border-radius: 3px;
+ overflow: hidden;
+
+ .progress-fill {
+ height: 100%;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border-radius: 3px;
+ transition: width 0.3s ease;
+ }
+ }
+
+ .progress-text {
+ font-size: 0.8rem;
+ color: #6b7280;
+ font-weight: 500;
+ min-width: 80px;
+ }
+ }
+}
+
+// Quick Actions
+.quick-actions-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 1rem;
+}
+
+.quick-action-btn {
+ background: white;
+ border: 1px solid #e5e7eb;
+ border-radius: 12px;
+ padding: 1.5rem;
+ cursor: pointer;
+ transition: all 0.3s ease;
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+ text-align: left;
+
+ &:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
+ border-color: #1e40af;
+ }
+
+ .action-icon {
+ width: 48px;
+ height: 48px;
+ background: rgba(30, 64, 175, 0.1);
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #1e40af;
+ flex-shrink: 0;
+
+ svg {
+ width: 24px;
+ height: 24px;
+ }
+ }
+
+ .action-content {
+ flex: 1;
+
+ .action-title {
+ font-size: 1.1rem;
+ font-weight: 600;
+ color: #1f2937;
+ margin-bottom: 0.25rem;
+ }
+
+ .action-description {
+ font-size: 0.9rem;
+ color: #6b7280;
+ }
+ }
+}
+
+// Mobile Responsive
+@media (max-width: 768px) {
+ .welcome-section {
+ flex-direction: column;
+ text-align: center;
+ gap: 1.5rem;
+
+ .welcome-content h1 {
+ font-size: 1.5rem;
+ }
+
+ .welcome-content p {
+ font-size: 1rem;
+ }
+ }
+
+ .stats-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .quick-actions-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .transaction-card {
+ padding: 1rem;
+
+ .transaction-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+
+ .transaction-details {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.5rem;
+ }
+ }
+}
+
+@media (max-width: 480px) {
+ .welcome-section {
+ padding: 1.5rem;
+
+ .welcome-content h1 {
+ font-size: 1.25rem;
+ }
+ }
+
+ .stat-card {
+ padding: 1rem;
+
+ .stat-icon {
+ width: 40px;
+ height: 40px;
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+
+ .stat-content .stat-value {
+ font-size: 1.5rem;
+ }
+ }
+
+ .quick-action-btn {
+ padding: 1rem;
+
+ .action-icon {
+ width: 40px;
+ height: 40px;
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+ }
+}
diff --git a/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.ts b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.ts
new file mode 100644
index 0000000..0fd3f88
--- /dev/null
+++ b/APP/src/app/portals/user-portal/pages/dashboard/dashboard.component.ts
@@ -0,0 +1,75 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { AuthService, User } from '../../../../shared/services/auth.service';
+
+interface Transaction {
+ id: string;
+ title: string;
+ amount: number;
+ status: 'active' | 'pending' | 'completed';
+ statusLabel: string;
+ date: Date;
+ progress: number;
+}
+
+@Component({
+ selector: 'app-dashboard',
+ standalone: true,
+ imports: [CommonModule],
+ templateUrl: './dashboard.component.html',
+ styleUrls: ['./dashboard.component.scss']
+})
+export class DashboardComponent implements OnInit {
+ currentUser: User | null = null;
+
+ activeTransactions = 5;
+ pendingTasks = 12;
+ completedTransactions = 23;
+ totalValue = 1250000;
+
+ recentTransactions: Transaction[] = [
+ {
+ id: 'RBJ-1001',
+ title: 'Maple Street Purchase Escrow',
+ amount: 425000,
+ status: 'active',
+ statusLabel: 'Open',
+ date: new Date('2026-04-24'),
+ progress: 68
+ },
+ {
+ id: 'RBJ-1002',
+ title: 'Oak Ridge Refinance',
+ amount: 310000,
+ status: 'pending',
+ statusLabel: 'Review',
+ date: new Date('2026-04-20'),
+ progress: 42
+ },
+ {
+ id: 'RBJ-1003',
+ title: 'Cedar Lane Closing',
+ amount: 515000,
+ status: 'completed',
+ statusLabel: 'Closed',
+ date: new Date('2026-04-12'),
+ progress: 100
+ }
+ ];
+
+ constructor(private authService: AuthService) { }
+
+ ngOnInit(): void {
+ this.authService.currentUser$.subscribe(user => {
+ this.currentUser = user;
+ });
+ }
+
+ getDisplayName(): string {
+ if (this.currentUser) {
+ const fullName = `${this.currentUser.firstName} ${this.currentUser.lastName}`.trim();
+ return fullName || this.currentUser.email;
+ }
+ return '';
+ }
+}
diff --git a/APP/src/app/portals/user-portal/user-portal.component.html b/APP/src/app/portals/user-portal/user-portal.component.html
new file mode 100644
index 0000000..fb11bae
--- /dev/null
+++ b/APP/src/app/portals/user-portal/user-portal.component.html
@@ -0,0 +1,122 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/portals/user-portal/user-portal.component.scss b/APP/src/app/portals/user-portal/user-portal.component.scss
new file mode 100644
index 0000000..67a59f3
--- /dev/null
+++ b/APP/src/app/portals/user-portal/user-portal.component.scss
@@ -0,0 +1,554 @@
+.user-portal-container {
+ min-height: 100vh;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ position: relative;
+ overflow: hidden;
+}
+
+// Background Shapes
+.background-shapes {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 0;
+}
+
+.shape {
+ position: absolute;
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.1);
+ animation: float 6s ease-in-out infinite;
+
+ &.shape-1 {
+ width: 200px;
+ height: 200px;
+ top: 10%;
+ left: 10%;
+ animation-delay: 0s;
+ }
+
+ &.shape-2 {
+ width: 150px;
+ height: 150px;
+ top: 60%;
+ right: 15%;
+ animation-delay: 2s;
+ }
+
+ &.shape-3 {
+ width: 100px;
+ height: 100px;
+ bottom: 20%;
+ left: 20%;
+ animation-delay: 4s;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-20px) rotate(180deg);
+ }
+}
+
+// Main Portal Layout
+.portal-layout {
+ display: flex;
+ min-height: 100vh;
+ position: relative;
+ z-index: 1;
+}
+
+// Desktop layout - fixed sidebar with scrolling content
+@media (min-width: 769px) {
+ .portal-layout {
+ .sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1000;
+ }
+
+ .main-content {
+ margin-left: 290px; // Account for fixed sidebar width
+ flex: 1;
+ }
+ }
+
+ // When sidebar is collapsed on desktop
+ .sidebar.collapsed + .main-content {
+ margin-left: 70px;
+ }
+}
+
+// Sidebar Overlay for Mobile
+.sidebar-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: 999;
+}
+
+// Sidebar Styles
+.sidebar {
+ width: 280px;
+ height: 100vh;
+ background: rgba(255, 255, 255, 0.95);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ border-right: 1px solid rgba(255, 255, 255, 0.2);
+ display: flex;
+ flex-direction: column;
+ transition: all 0.3s ease;
+ box-shadow: 2px 0 20px rgba(0, 0, 0, 0.1);
+ flex-shrink: 0;
+
+ &.collapsed {
+ width: 70px;
+
+ .logo-text,
+ .nav-section h4,
+ .nav-item span,
+ .user-details,
+ .logout-btn span {
+ opacity: 0;
+ visibility: hidden;
+ }
+
+ .sidebar-footer {
+ padding: 1rem 0.5rem;
+ }
+
+ .user-info {
+ justify-content: center;
+ }
+ }
+}
+
+.sidebar-header {
+ padding: 1.5rem;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.logo-section {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+
+ .logo-image {
+ height: 40px;
+ width: auto;
+ //filter: brightness(0) saturate(100%) invert(27%) sepia(51%) saturate(2878%) hue-rotate(346deg) brightness(104%)contrast(97%);
+ }
+
+ .logo-text {
+ h2 {
+ margin: 0;
+ font-size: 1.25rem;
+ font-weight: 700;
+ color: #1e3a8a;
+ letter-spacing: -0.01em;
+ }
+
+ .tagline {
+ font-size: 0.75rem;
+ color: #6b7280;
+ font-weight: 500;
+ }
+ }
+}
+
+.sidebar-toggle {
+ background: none;
+ border: none;
+ padding: 0.5rem;
+ border-radius: 8px;
+ cursor: pointer;
+ color: #6b7280;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ background: #f3f4f6;
+ color: #1e40af;
+ }
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+}
+
+.sidebar-nav {
+ flex: 1;
+ padding: 1rem 0;
+ overflow-y: auto;
+ max-height: calc(100vh - 200px); // Account for header and footer
+}
+
+.nav-section {
+ margin-bottom: 2rem;
+
+ h4 {
+ margin: 0 0 1rem 1.5rem;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ color: #6b7280;
+ }
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ padding: 0.75rem 1.5rem;
+ color: #6b7280;
+ text-decoration: none;
+ transition: all 0.2s ease;
+ position: relative;
+ margin: 0.125rem 0;
+
+ &:hover {
+ background: rgba(30, 64, 175, 0.1);
+ color: #1e40af;
+ }
+
+ &.active {
+ background: rgba(30, 64, 175, 0.15);
+ color: #1e40af;
+ font-weight: 600;
+
+ &::before {
+ content: "";
+ position: absolute;
+ left: 0;
+ top: 0;
+ bottom: 0;
+ width: 3px;
+ background: #1e40af;
+ }
+ }
+
+ .nav-icon {
+ width: 20px;
+ height: 20px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-shrink: 0;
+
+ svg {
+ width: 100%;
+ height: 100%;
+ }
+ }
+
+ span {
+ font-size: 0.9rem;
+ font-weight: 500;
+ }
+
+ .nav-badge {
+ background: #ef4444;
+ color: white;
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.25rem 0.5rem;
+ border-radius: 10px;
+ margin-left: auto;
+ min-width: 20px;
+ text-align: center;
+ }
+}
+
+.sidebar-footer {
+ padding: 1.5rem;
+ border-top: 1px solid rgba(0, 0, 0, 0.1);
+}
+
+.user-info {
+ display: flex;
+ align-items: center;
+ gap: 0.75rem;
+ margin-bottom: 1rem;
+
+ .user-avatar {
+ width: 40px;
+ height: 40px;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ flex-shrink: 0;
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+ }
+
+ .user-details {
+ flex: 1;
+ min-width: 0;
+
+ .user-name {
+ font-weight: 600;
+ color: #1f2937;
+ font-size: 0.9rem;
+ margin-bottom: 0.125rem;
+ }
+
+ .user-email {
+ font-size: 0.8rem;
+ color: #6b7280;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
+}
+
+.logout-btn {
+ width: 100%;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.75rem;
+ background: none;
+ border: 1px solid #e5e7eb;
+ border-radius: 8px;
+ color: #6b7280;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ font-size: 0.9rem;
+ font-weight: 500;
+
+ &:hover {
+ background: #fef2f2;
+ border-color: #fecaca;
+ color: #dc2626;
+ }
+
+ svg {
+ width: 16px;
+ height: 16px;
+ }
+}
+
+// Main Content Area
+.main-content {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ background: rgba(255, 255, 255, 0.95);
+ -webkit-backdrop-filter: blur(10px);
+ backdrop-filter: blur(10px);
+ margin: 1rem;
+ border-radius: 16px;
+ box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ height: calc(100vh - 2rem);
+ max-height: calc(100vh - 2rem);
+}
+
+.top-header {
+ padding: 1.5rem 2rem;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.1);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ background: rgba(255, 255, 255, 0.8);
+}
+
+.header-left {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.mobile-menu-btn {
+ display: none;
+ background: none;
+ border: none;
+ padding: 0.5rem;
+ border-radius: 8px;
+ cursor: pointer;
+ color: #6b7280;
+ transition: all 0.2s ease;
+
+ &:hover {
+ background: #f3f4f6;
+ color: #1e40af;
+ }
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+}
+
+.breadcrumb {
+ .breadcrumb-item {
+ font-size: 1.25rem;
+ font-weight: 600;
+ color: #1f2937;
+ }
+}
+
+.header-right {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.action-btn {
+ position: relative;
+ background: none;
+ border: none;
+ padding: 0.75rem;
+ border-radius: 8px;
+ cursor: pointer;
+ color: #6b7280;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ &:hover {
+ background: #f3f4f6;
+ color: #1e40af;
+ }
+
+ svg {
+ width: 20px;
+ height: 20px;
+ }
+
+ .notification-badge {
+ position: absolute;
+ top: 0.25rem;
+ right: 0.25rem;
+ background: #ef4444;
+ color: white;
+ font-size: 0.75rem;
+ font-weight: 600;
+ padding: 0.125rem 0.375rem;
+ border-radius: 10px;
+ min-width: 18px;
+ text-align: center;
+ }
+}
+
+.page-content {
+ flex: 1;
+ padding: 2rem;
+ overflow-y: auto;
+ height: 100%;
+ max-height: 100%;
+}
+
+// Mobile Responsive
+@media (max-width: 768px) {
+ .portal-layout {
+ flex-direction: column;
+ }
+
+ .sidebar {
+ position: fixed;
+ top: 0;
+ left: 0;
+ z-index: 1000;
+ transform: translateX(-100%);
+ transition: transform 0.3s ease;
+
+ &:not(.collapsed) {
+ transform: translateX(0);
+ }
+
+ &.collapsed {
+ transform: translateX(-100%);
+ }
+ }
+
+ .main-content {
+ margin: 0;
+ border-radius: 0;
+ height: 100vh;
+ max-height: 100vh;
+ }
+
+ .mobile-menu-btn {
+ display: block;
+ }
+
+ .page-content {
+ padding: 1rem;
+ }
+
+ .top-header {
+ padding: 1rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .page-content {
+ padding: 0.75rem;
+ }
+
+ .top-header {
+ padding: 0.75rem;
+ }
+
+ .breadcrumb .breadcrumb-item {
+ font-size: 1.1rem;
+ }
+}
+
+// Overlay for mobile sidebar
+@media (max-width: 768px) {
+ .sidebar:not(.collapsed)::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.5);
+ z-index: -1;
+ }
+}
+
+// Desktop sidebar collapsed state
+@media (min-width: 769px) {
+ .sidebar.collapsed {
+ width: 70px;
+ }
+
+ .main-content.sidebar-collapsed {
+ margin-left: 70px;
+ }
+}
diff --git a/APP/src/app/portals/user-portal/user-portal.component.ts b/APP/src/app/portals/user-portal/user-portal.component.ts
new file mode 100644
index 0000000..1530697
--- /dev/null
+++ b/APP/src/app/portals/user-portal/user-portal.component.ts
@@ -0,0 +1,139 @@
+import { Component, OnInit, OnDestroy, HostListener } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, NavigationEnd, RouterModule, RouterLink, RouterLinkActive } from '@angular/router';
+import { RouterOutlet } from '@angular/router';
+import { AuthService, User } from '../../shared/services/auth.service';
+import { Subject, takeUntil, filter } from 'rxjs';
+
+@Component({
+ selector: 'app-user-portal',
+ standalone: true,
+ imports: [
+ CommonModule,
+ RouterModule,
+ RouterLink,
+ RouterLinkActive,
+ RouterOutlet
+ ],
+ templateUrl: './user-portal.component.html',
+ styleUrls: ['./user-portal.component.scss']
+})
+export class UserPortalComponent implements OnInit, OnDestroy {
+ sidebarCollapsed = false;
+ isMobile = false;
+ currentUser: User | null = null;
+ currentPageTitle = 'Dashboard';
+ unreadMessages = 3;
+ unreadNotifications = 2;
+
+ private destroy$ = new Subject();
+
+ constructor(
+ private authService: AuthService,
+ private router: Router
+ ) { }
+
+ ngOnInit(): void {
+ this.checkScreenSize();
+ this.setupUserSubscription();
+ this.setupRouteSubscription();
+ }
+
+ ngOnDestroy(): void {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ @HostListener('window:resize', ['$event'])
+ onResize(event: any): void {
+ this.checkScreenSize();
+ }
+
+ private checkScreenSize(): void {
+ this.isMobile = window.innerWidth <= 768;
+ if (this.isMobile) {
+ this.sidebarCollapsed = true;
+ } else {
+ // On desktop, start with sidebar expanded
+ this.sidebarCollapsed = false;
+ }
+ }
+
+ private setupUserSubscription(): void {
+ this.authService.currentUser$
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(user => {
+ this.currentUser = user;
+ });
+ }
+
+ private setupRouteSubscription(): void {
+ this.router.events
+ .pipe(
+ filter(event => event instanceof NavigationEnd),
+ takeUntil(this.destroy$)
+ )
+ .subscribe(() => {
+ this.updatePageTitle();
+ });
+ }
+
+ private updatePageTitle(): void {
+ const url = this.router.url;
+ const segments = url.split('/').filter(segment => segment);
+
+ if (segments.length >= 2) {
+ const page = segments[1];
+ this.currentPageTitle = this.getPageTitle(page);
+ } else {
+ this.currentPageTitle = 'Dashboard';
+ }
+ }
+
+ private getPageTitle(page: string): string {
+ const titles: { [key: string]: string } = {
+ 'dashboard': 'Dashboard',
+ 'transactions': 'Escrow Transactions',
+ 'tasks': 'Tasks & Todos',
+ 'contacts': 'Contacts',
+ 'documents': 'Documents',
+ 'messages': 'Messages',
+ 'settings': 'Settings'
+ };
+
+ return titles[page] || 'Dashboard';
+ }
+
+ toggleSidebar(): void {
+ this.sidebarCollapsed = !this.sidebarCollapsed;
+ }
+
+ get mainContentClass(): string {
+ return this.sidebarCollapsed ? 'main-content sidebar-collapsed' : 'main-content';
+ }
+
+ onSidebarOverlayClick(): void {
+ if (this.isMobile && !this.sidebarCollapsed) {
+ this.sidebarCollapsed = true;
+ }
+ }
+
+ onNavigationClick(): void {
+ if (this.isMobile) {
+ this.sidebarCollapsed = true;
+ }
+ }
+
+ logout(): void {
+ this.authService.logout();
+ this.router.navigate(['/login']);
+ }
+
+ getDisplayName(): string {
+ if (this.currentUser) {
+ const fullName = `${this.currentUser.firstName} ${this.currentUser.lastName}`.trim();
+ return fullName || this.currentUser.email;
+ }
+ return '';
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/mfa-dialog/mfa-dialog.component.html b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.html
new file mode 100644
index 0000000..00f79fb
--- /dev/null
+++ b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.html
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Invalid code. Please try again.
+
+
+
+
+
+
0" class="resend-button">
+
+
+
+
+
+
+ {{ resendCodeText }}
+
+
+
+
+
+
+
+
+
+ {{ processing ? 'Verifying...' : 'Verify Code' }}
+
+
+
+
+
+
+
Didn't receive the code? Check your spam folder or try resending.
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/shared/mfa-dialog/mfa-dialog.component.scss b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.scss
new file mode 100644
index 0000000..5fe4843
--- /dev/null
+++ b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.scss
@@ -0,0 +1,418 @@
+.mfa-dialog-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ padding: 1rem;
+ -webkit-backdrop-filter: blur(8px);
+ backdrop-filter: blur(8px);
+}
+
+.mfa-dialog-container {
+ position: relative;
+ width: 100%;
+ max-width: 450px;
+ background: rgba(255, 255, 255, 0.98);
+ border-radius: 20px;
+ box-shadow: 0 25px 50px rgba(0, 0, 0, 0.25);
+ -webkit-backdrop-filter: blur(20px);
+ backdrop-filter: blur(20px);
+ overflow: hidden;
+ animation: dialogSlideIn 0.3s ease-out;
+}
+
+@keyframes dialogSlideIn {
+ from {
+ opacity: 0;
+ transform: translateY(-20px) scale(0.95);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+}
+
+// Background Shapes
+.dialog-background-shapes {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ overflow: hidden;
+ z-index: 0;
+}
+
+.shape {
+ position: absolute;
+ border-radius: 50%;
+ background: rgba(30, 64, 175, 0.05);
+ animation: float 8s ease-in-out infinite;
+
+ &.shape-1 {
+ width: 80px;
+ height: 80px;
+ top: 15%;
+ right: 10%;
+ animation-delay: 0s;
+ }
+
+ &.shape-2 {
+ width: 60px;
+ height: 60px;
+ bottom: 20%;
+ left: 15%;
+ animation-delay: 3s;
+ }
+}
+
+@keyframes float {
+ 0%,
+ 100% {
+ transform: translateY(0px) rotate(0deg);
+ }
+ 50% {
+ transform: translateY(-15px) rotate(180deg);
+ }
+}
+
+// Dialog Content
+.mfa-dialog-content {
+ position: relative;
+ z-index: 1;
+ padding: 2rem;
+}
+
+// Header
+.mfa-header {
+ position: relative;
+ text-align: center;
+ margin-bottom: 2rem;
+
+ .close-button {
+ position: absolute;
+ top: -0.5rem;
+ right: -0.5rem;
+ background: #f8f9fa;
+ border: none;
+ border-radius: 50%;
+ width: 32px;
+ height: 32px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ color: #6b7280;
+
+ &:hover {
+ background: #e5e7eb;
+ color: #374151;
+ transform: scale(1.1);
+ }
+
+ svg {
+ width: 16px;
+ height: 16px;
+ }
+ }
+
+ .header-content {
+ .mfa-icon {
+ width: 60px;
+ height: 60px;
+ margin: 0 auto 1rem;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ box-shadow: 0 8px 25px rgba(30, 58, 138, 0.3);
+
+ svg {
+ width: 28px;
+ height: 28px;
+ }
+ }
+
+ h3 {
+ font-size: 1.75rem;
+ font-weight: 700;
+ color: #1a1a1a;
+ margin: 0 0 0.5rem 0;
+ letter-spacing: -0.01em;
+ }
+
+ p {
+ color: #6b7280;
+ font-size: 1rem;
+ margin: 0;
+ line-height: 1.5;
+ }
+ }
+}
+
+// MFA Code Section
+.mfa-code-section {
+ margin-bottom: 2rem;
+
+ .code-inputs-container {
+ display: flex;
+ gap: 0.75rem;
+ justify-content: center;
+ margin-bottom: 1rem;
+
+ .mfa-input {
+ width: 50px;
+ height: 50px;
+ border: 2px solid #e5e7eb;
+ border-radius: 12px;
+ text-align: center;
+ font-size: 1.5rem;
+ font-weight: 600;
+ color: #1a1a1a;
+ background: #ffffff;
+ transition: all 0.2s ease;
+ outline: none;
+
+ &:focus {
+ border-color: #1e40af;
+ box-shadow: 0 0 0 3px rgba(30, 64, 175, 0.1);
+ transform: scale(1.05);
+ }
+
+ &:hover:not(:focus) {
+ border-color: #d1d5db;
+ }
+
+ &::-webkit-outer-spin-button,
+ &::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+ }
+
+ &[type="number"] {
+ -moz-appearance: textfield;
+ }
+ }
+ }
+
+ .error-message {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ background: #fee2e2;
+ color: #dc2626;
+ padding: 0.75rem 1rem;
+ border-radius: 12px;
+ border: 1px solid #fecaca;
+ font-size: 0.9rem;
+ font-weight: 500;
+ animation: errorShake 0.5s ease-in-out;
+
+ .error-icon {
+ width: 16px;
+ height: 16px;
+ flex-shrink: 0;
+ }
+ }
+}
+
+@keyframes errorShake {
+ 0%,
+ 100% {
+ transform: translateX(0);
+ }
+ 25% {
+ transform: translateX(-5px);
+ }
+ 75% {
+ transform: translateX(5px);
+ }
+}
+
+// Actions
+.mfa-actions {
+ display: flex;
+ gap: 1rem;
+ margin-bottom: 1.5rem;
+
+ .resend-button {
+ flex: 1;
+ height: 48px;
+ border-radius: 12px;
+ font-size: 0.95rem;
+ font-weight: 600;
+ background: #f8f9fa;
+ border: 2px solid #e5e7eb;
+ color: #6b7280;
+ transition: all 0.3s ease;
+
+ &:hover:not(:disabled) {
+ background: #e9ecef;
+ border-color: #d1d5db;
+ color: #495057;
+ transform: translateY(-1px);
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+ }
+
+ .button-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ }
+
+ .button-icon {
+ width: 16px;
+ height: 16px;
+ }
+ }
+
+ .verify-button {
+ flex: 2;
+ height: 48px;
+ border-radius: 12px;
+ font-size: 1rem;
+ font-weight: 600;
+ background: linear-gradient(135deg, #1e3a8a 0%, #1e40af 50%, #3b82f6 100%);
+ border: none;
+ color: white;
+ box-shadow: 0 8px 25px rgba(30, 58, 138, 0.3);
+ transition: all 0.3s ease;
+
+ &:hover:not(:disabled) {
+ transform: translateY(-2px);
+ box-shadow: 0 12px 35px rgba(30, 58, 138, 0.4);
+ }
+
+ &:disabled {
+ opacity: 0.6;
+ cursor: not-allowed;
+ transform: none;
+ }
+
+ .button-content {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ }
+
+ .button-icon {
+ width: 18px;
+ height: 18px;
+ }
+ }
+}
+
+// Help Text
+.help-text {
+ text-align: center;
+
+ p {
+ color: #9ca3af;
+ font-size: 0.85rem;
+ margin: 0;
+ line-height: 1.4;
+ }
+}
+
+// Mobile Responsive
+@media (max-width: 480px) {
+ .mfa-dialog-overlay {
+ padding: 0.5rem;
+ }
+
+ .mfa-dialog-container {
+ max-width: 100%;
+ border-radius: 16px;
+ }
+
+ .mfa-dialog-content {
+ padding: 1.5rem;
+ }
+
+ .mfa-header {
+ margin-bottom: 1.5rem;
+
+ .header-content {
+ .mfa-icon {
+ width: 50px;
+ height: 50px;
+ margin-bottom: 0.75rem;
+
+ svg {
+ width: 24px;
+ height: 24px;
+ }
+ }
+
+ h3 {
+ font-size: 1.5rem;
+ }
+
+ p {
+ font-size: 0.9rem;
+ }
+ }
+ }
+
+ .mfa-code-section {
+ margin-bottom: 1.5rem;
+
+ .code-inputs-container {
+ gap: 0.5rem;
+
+ .mfa-input {
+ width: 45px;
+ height: 45px;
+ font-size: 1.25rem;
+ }
+ }
+ }
+
+ .mfa-actions {
+ flex-direction: column;
+ gap: 0.75rem;
+
+ .resend-button,
+ .verify-button {
+ flex: 1;
+ height: 44px;
+ }
+
+ .verify-button {
+ order: -1;
+ }
+ }
+}
+
+@media (max-width: 360px) {
+ .mfa-dialog-content {
+ padding: 1rem;
+ }
+
+ .code-inputs-container {
+ gap: 0.4rem;
+
+ .mfa-input {
+ width: 40px;
+ height: 40px;
+ font-size: 1.1rem;
+ }
+ }
+}
diff --git a/APP/src/app/shared/mfa-dialog/mfa-dialog.component.ts b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.ts
new file mode 100644
index 0000000..7905336
--- /dev/null
+++ b/APP/src/app/shared/mfa-dialog/mfa-dialog.component.ts
@@ -0,0 +1,250 @@
+import { Component, ElementRef, QueryList, ViewChildren, Output, EventEmitter, Input } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { AuthService, LoginCredentials, LoginResultType } from '../services/auth.service';
+import { take } from 'rxjs/operators';
+
+const CODE_LENGTH = 6;
+
+@Component({
+ selector: 'app-mfa-dialog',
+ standalone: true,
+ imports: [
+ CommonModule,
+ FormsModule,
+ ButtonsModule,
+ IndicatorsModule
+ ],
+ templateUrl: './mfa-dialog.component.html',
+ styleUrls: ['./mfa-dialog.component.scss']
+})
+export class MfaDialogComponent {
+ @ViewChildren('codeInput') codeInputs!: QueryList;
+ @Output() mfaSuccess = new EventEmitter();
+ @Output() mfaCancel = new EventEmitter();
+ @Input() visible = false;
+
+ token: string = '';
+ userInputCodes: (string | null)[] = [];
+ userInputCodes2: (string | null)[] = [];
+ loginData!: LoginCredentials;
+ processing = false;
+ allowSubmit = false;
+ isInvalidCode = false;
+ resendCountDown = 30;
+
+ constructor(private authService: AuthService) { }
+
+ ngOnInit() {
+ for (let i = 0; i < CODE_LENGTH; i++) {
+ this.userInputCodes.push(null);
+ this.userInputCodes2.push(null);
+ }
+ this.setReSendCountDown();
+ }
+
+ pasteCode(index: number, event: ClipboardEvent) {
+ event.preventDefault();
+ const data = event.clipboardData?.getData('text/plain') || '';
+ let pasteCode = data.replace(new RegExp("[^0-9]", 'g'), "");
+ for (let i = index; i < CODE_LENGTH; i++) {
+ if (pasteCode.length > i) {
+ const code = pasteCode[i];
+ let input = this.codeInputs.find((element, j) => j === i);
+ if (input) {
+ input.nativeElement.value = code;
+ this.userInputCodes[i] = code;
+ }
+ }
+ }
+ this.validate(5);
+ }
+
+ public onKeyDown(index: number, e: KeyboardEvent): void {
+ const el: HTMLInputElement = e.target as HTMLInputElement;
+ if (e.ctrlKey && e.key.toUpperCase() == 'V') {
+ return;
+ } else {
+ let nextFocusInput: ElementRef | undefined = undefined;
+ switch (e.key) {
+ case 'ArrowLeft':
+ nextFocusInput = this.getInputElements(index, -1);
+ if (nextFocusInput) nextFocusInput.nativeElement.focus();
+ break;
+ case 'ArrowRight':
+ nextFocusInput = this.getInputElements(index, 1);
+ if (nextFocusInput) nextFocusInput.nativeElement.focus();
+ break;
+ case 'Backspace':
+ if (el.value) {
+ el.value = '';
+ } else {
+ nextFocusInput = this.getInputElements(index, -1);
+ }
+ break;
+ case 'Delete':
+ el.value = '';
+ break;
+ default:
+ break;
+ }
+
+ if (nextFocusInput) {
+ nextFocusInput.nativeElement.focus();
+ return;
+ }
+
+ let isReplacing = el.selectionStart != el.selectionEnd;
+
+ if (this.isEditingKeyPress(e)) {
+ e.preventDefault();
+ if (new RegExp("[0-9]", 'g').test(e.key)) {
+ this.userInputCodes[index] = e.key;
+ el.value = e.key;
+ let nextInput = this.getInputElements(index, 1);
+ if (nextInput) nextInput.nativeElement.focus();
+ } else {
+ el.value = '';
+ this.userInputCodes[index] = null;
+ }
+ }
+ }
+
+ this.isInvalidCode = false;
+ this.validate(index);
+ }
+
+ private getInputElements(index: number, indexOffset: number): ElementRef | undefined {
+ let nextInput = this.codeInputs.find((element, i) => i === (index + indexOffset));
+ return nextInput;
+ }
+
+ private isEditingKeyPress(e: KeyboardEvent): boolean {
+ return e.key.length === 1 || e.key === 'Backspace' || e.key === 'Delete' || e.key === 'ArrowLeft' || e.key === 'ArrowRight';
+ }
+
+ validate(focusIndex: number) {
+ this.allowSubmit = !this.userInputCodes.some(n => n == null);
+ this.token = this.userInputCodes.map(n => n != null ? n : '').join('');
+ if (this.token && this.token.length == 6 && focusIndex == 5) {
+ this.submitCode();
+ }
+ }
+
+ close() {
+ this.visible = false;
+ this.mfaCancel.emit();
+ }
+
+ show() {
+ this.visible = true;
+ this.resetForm();
+ }
+
+ hide() {
+ this.visible = false;
+ }
+
+ resetForm() {
+ this.userInputCodes = [];
+ this.userInputCodes2 = [];
+ this.token = '';
+ this.isInvalidCode = false;
+ this.processing = false;
+ this.allowSubmit = false;
+
+ for (let i = 0; i < CODE_LENGTH; i++) {
+ this.userInputCodes.push(null);
+ this.userInputCodes2.push(null);
+ }
+
+ // Focus on first input
+ setTimeout(() => {
+ const firstInput = document.querySelector('.mfa-input:first-child') as HTMLInputElement;
+ if (firstInput) {
+ firstInput.focus();
+ }
+ }, 100);
+ }
+
+ submitCode() {
+ this.processing = true;
+ this.loginData.mfaCode = this.token;
+
+ // Check if this is token-based authentication
+ if ((this.loginData as any).tokenUser) {
+ // Handle token-based MFA verification
+ this.authService.verifyMfaForToken(this.token, (this.loginData as any).tokenUser).subscribe({
+ next: (result) => {
+ this.processing = false;
+
+ if (result.result === LoginResultType.Success) {
+ this.mfaSuccess.emit(result.responseData);
+ this.visible = false;
+ } else {
+ this.isInvalidCode = true;
+ }
+ },
+ error: (error) => {
+ this.processing = false;
+ this.isInvalidCode = true;
+ console.error('MFA verification error:', error);
+ }
+ });
+ } else {
+ // Handle regular login MFA verification
+ this.authService.login(this.loginData).subscribe({
+ next: (result) => {
+ this.processing = false;
+
+ if (result.result === LoginResultType.Success) {
+ this.mfaSuccess.emit(result.responseData);
+ this.visible = false;
+ } else {
+ this.isInvalidCode = true;
+ }
+ },
+ error: (error) => {
+ this.processing = false;
+ this.isInvalidCode = true;
+ console.error('MFA verification error:', error);
+ }
+ });
+ }
+ }
+
+ setReSendCountDown() {
+ if (this.resendCountDown > 0) {
+ setTimeout(() => {
+ this.resendCountDown--;
+ this.setReSendCountDown();
+ }, 1000);
+ }
+ }
+
+ resendMFCode() {
+ this.resendCountDown = 30;
+ this.loginData.mfaCode = '';
+
+ // Simulate resend MFA code - replace with actual service call
+ console.log('Resending MFA code to:', this.loginData.email);
+ // Check if this is token-based authentication
+ if ((this.loginData as any).tokenUser) {
+ // Handle token-based MFA verification
+ this.authService.verifyMfaForToken(this.token, (this.loginData as any).tokenUser).pipe(
+ take(1)
+ ).subscribe(result => {
+ this.setReSendCountDown();
+ });
+ } else {
+ //TODO: Implement resend MFA code for regular login
+ }
+ this.setReSendCountDown();
+ }
+
+ public get resendCodeText(): string {
+ return 'Resend Code' + (this.resendCountDown > 0 ? ` (${this.resendCountDown})` : '');
+ }
+}
diff --git a/APP/src/app/shared/models/index.ts b/APP/src/app/shared/models/index.ts
new file mode 100644
index 0000000..a212b7c
--- /dev/null
+++ b/APP/src/app/shared/models/index.ts
@@ -0,0 +1,2 @@
+// Export user models
+export * from './user.model';
\ No newline at end of file
diff --git a/APP/src/app/shared/models/user.model.ts b/APP/src/app/shared/models/user.model.ts
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/app/shared/services/auth.service.ts b/APP/src/app/shared/services/auth.service.ts
new file mode 100644
index 0000000..f9924f7
--- /dev/null
+++ b/APP/src/app/shared/services/auth.service.ts
@@ -0,0 +1,400 @@
+import { Injectable } from '@angular/core';
+import { HttpClient, HttpHeaders } from '@angular/common/http';
+import { BehaviorSubject, Observable, of } from 'rxjs';
+import { map, catchError } from 'rxjs/operators';
+import { ApiConfigService } from '../../core/services/api-config.service';
+
+export interface User {
+ id: string;
+ username: string;
+ email: string;
+ firstName: string;
+ lastName: string;
+ createdAt: string;
+ branchIds: string[];
+ token: string;
+ mfaVerified?: boolean;
+}
+
+export interface LoginCredentials {
+ email: string;
+ password: string;
+ mfaCode?: string;
+}
+
+export interface LoginResponse {
+ isAuthenticated: boolean;
+ requiresMfa: boolean;
+ token?: string;
+ expires?: string;
+ user?: UserDto;
+ message?: string;
+}
+
+export interface TokenCreateResponse {
+ token?: string;
+ mfaToken?: string;
+ access?: AccessDto[];
+ isAuthenticated?: boolean;
+ isAuthorized?: boolean;
+ isChangePassword?: boolean;
+ message?: string;
+ username?: string;
+ email?: string;
+ concurrentTabs?: number;
+ mfaType?: number;
+ mfaHint?: string;
+ Token?: string;
+ MfaToken?: string;
+ Access?: AccessDto[];
+ IsAuthenticated?: boolean;
+ IsAuthorized?: boolean;
+ IsChangePassword?: boolean;
+ Message?: string;
+ Username?: string;
+ Email?: string;
+ ConcurrentTabs?: number;
+ MfaType?: number;
+ MfaHint?: string;
+}
+
+export interface AccessDto {
+ branchName?: string;
+ redxBranch?: string;
+ BranchName?: string;
+ RedxBranch?: string;
+}
+
+export interface UserDto {
+ id: string;
+ username: string;
+ email: string;
+ firstName: string;
+ lastName: string;
+ createdAt: string;
+ branchIds: string[];
+}
+
+export enum LoginResultType {
+ Success = 'Success',
+ MfaRequired = 'MfaRequired',
+ InvalidCredentials = 'InvalidCredentials',
+ Error = 'Error'
+}
+
+export interface LoginResult {
+ result: LoginResultType;
+ responseData?: User;
+ message?: string;
+}
+
+export interface TokenVerificationResult {
+ isValid: boolean;
+ user?: User;
+ message?: string;
+ expiresAt?: Date;
+ requiresMfa?: boolean;
+}
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthService {
+ private currentUserSubject = new BehaviorSubject(null);
+ public currentUser$ = this.currentUserSubject.asObservable();
+ private redirectUrl: string = '/dashboard';
+ constructor(
+ private http: HttpClient,
+ private apiConfig: ApiConfigService
+ ) { }
+
+ login(credentials: LoginCredentials): Observable {
+ return this.authenticateUser(credentials);
+ }
+
+ private authenticateUser(credentials: LoginCredentials): Observable {
+ const loginUrl = `${this.apiConfig.tokenUrl}/Create`;
+ return this.http.get(loginUrl, {
+ headers: this.buildTokenCreateHeaders(credentials)
+ }).pipe(
+ map((response: TokenCreateResponse) => this.mapTokenCreateResponse(response, credentials)),
+ catchError((error) => {
+ console.error('Login error:', error);
+ return of({
+ result: LoginResultType.Error,
+ message: error.error?.message || 'An error occurred during login'
+ });
+ })
+ );
+ }
+
+ private buildTokenCreateHeaders(credentials: LoginCredentials): HttpHeaders {
+ let headers = new HttpHeaders({
+ Authorization: `Basic ${btoa(`${credentials.email}:${credentials.password}`)}`
+ });
+
+ if (credentials.mfaCode) {
+ headers = headers.set('mfaCode', credentials.mfaCode);
+ }
+
+ return headers;
+ }
+
+ private mapTokenCreateResponse(response: TokenCreateResponse, credentials: LoginCredentials): LoginResult {
+ const token = response.token || response.Token || '';
+ const message = response.message || response.Message || '';
+ const isAuthenticated = response.isAuthenticated ?? response.IsAuthenticated ?? false;
+ const isAuthorized = response.isAuthorized ?? response.IsAuthorized ?? false;
+
+ if (isAuthenticated && isAuthorized && token) {
+ return {
+ result: LoginResultType.Success,
+ responseData: this.mapUserFromTokenCreateResponse(response, credentials, token)
+ };
+ }
+
+ if (isAuthenticated && !token && this.isMfaRequired(message)) {
+ return {
+ result: LoginResultType.MfaRequired,
+ responseData: this.mapUserFromTokenCreateResponse(
+ response,
+ credentials,
+ response.mfaToken || response.MfaToken || ''
+ ),
+ message
+ };
+ }
+
+ return {
+ result: isAuthenticated ? LoginResultType.Error : LoginResultType.InvalidCredentials,
+ message: message || 'Invalid email or password'
+ };
+ }
+
+ private mapUserFromTokenCreateResponse(
+ response: TokenCreateResponse,
+ credentials: LoginCredentials,
+ token: string
+ ): User {
+ const username = response.username || response.Username || credentials.email;
+ const email = response.email || response.Email || credentials.email;
+ const access = response.access || response.Access || [];
+
+ return {
+ id: username || email,
+ username,
+ email,
+ firstName: '',
+ lastName: '',
+ createdAt: new Date().toISOString(),
+ branchIds: access
+ .map(item => item.redxBranch || item.RedxBranch || item.branchName || item.BranchName || '')
+ .filter(branchId => !!branchId),
+ token,
+ mfaVerified: !!credentials.mfaCode
+ };
+ }
+
+ private isMfaRequired(message: string): boolean {
+ return message.toLowerCase().includes('mfa verification required');
+ }
+
+ logout(): void {
+ this.currentUserSubject.next(null);
+ this.redirectUrl = '/dashboard';
+ // Clear any stored tokens, etc.
+ localStorage.removeItem('currentUser');
+ }
+
+ getCurrentUser(): User | null {
+ return this.currentUserSubject.value;
+ }
+
+ getToken(): string | null {
+ const user = this.currentUserSubject.value;
+ return user?.token || null;
+ }
+
+ isAuthenticated(): boolean {
+ return this.currentUserSubject.value !== null;
+ }
+
+ setCurrentUser(user: User): void {
+ this.currentUserSubject.next(user);
+ // Store user in localStorage for persistence
+ localStorage.setItem('currentUser', JSON.stringify(user));
+ }
+
+ setRedirectUrl(url: string): void {
+ this.redirectUrl = url;
+ }
+
+ getRedirectUrl(): string {
+ return this.redirectUrl;
+ }
+
+ // Initialize user from localStorage on app start
+ initializeAuth(): void {
+ const storedUser = localStorage.getItem('currentUser');
+ if (storedUser) {
+ try {
+ const user = JSON.parse(storedUser);
+ this.currentUserSubject.next(user);
+ } catch (error) {
+ console.error('Error parsing stored user:', error);
+ localStorage.removeItem('currentUser');
+ }
+ }
+ }
+
+ /**
+ * Verify JWT token from email link (local verification)
+ * @param token The JWT token to verify
+ * @returns Observable with verification result
+ */
+ verifySecretLinkToken(token: string): Observable {
+ try {
+ // Parse JWT token locally
+ const tokenData = this.parseJwtToken(token);
+
+ if (!tokenData) {
+ return of({
+ isValid: false,
+ message: 'Invalid token format'
+ });
+ }
+
+ // Check if token is expired
+ if (this.isTokenExpired(token)) {
+ return of({
+ isValid: false,
+ message: 'This link has expired. Please request a new one.'
+ });
+ }
+ console.log('tokenData', tokenData);
+
+ // Extract user data from token
+ const user: User = {
+ id: tokenData.userId || tokenData.sub || tokenData.id,
+ username: tokenData.username || tokenData.preferred_username || '',
+ email: tokenData.email || tokenData.email_address || '',
+ firstName: tokenData.firstName || tokenData.given_name || tokenData.first_name || '',
+ lastName: tokenData.lastName || tokenData.family_name || tokenData.last_name || '',
+ createdAt: tokenData.createdAt || tokenData.created_at || new Date().toISOString(),
+ branchIds: tokenData.branchIds || tokenData.branch_ids || [],
+ token: token,
+ mfaVerified: false // Token users still need MFA verification
+ };
+
+ return of({
+ isValid: true,
+ user: user,
+ message: 'Token verified successfully. MFA required.',
+ expiresAt: tokenData.exp ? new Date(tokenData.exp * 1000) : undefined,
+ requiresMfa: true
+ });
+
+ } catch (error) {
+ console.error('Token verification error:', error);
+ return of({
+ isValid: false,
+ message: 'Invalid or corrupted token'
+ });
+ }
+ }
+
+ /**
+ * Check if a token is expired locally (basic check)
+ * @param token JWT token to check
+ * @returns boolean indicating if token is expired
+ */
+ isTokenExpired(token: string): boolean {
+ try {
+ const payload = JSON.parse(atob(token.split('.')[1]));
+ const currentTime = Math.floor(Date.now() / 1000);
+ return payload.exp < currentTime;
+ } catch (error) {
+ console.error('Error parsing token:', error);
+ return true; // Consider invalid tokens as expired
+ }
+ }
+
+ /**
+ * Parse JWT token and extract payload
+ * @param token JWT token to parse
+ * @returns parsed token data or null if invalid
+ */
+ private parseJwtToken(token: string): any | null {
+ try {
+ // Split token into parts
+ const parts = token.split('.');
+ if (parts.length !== 3) {
+ return null;
+ }
+
+ // Decode the payload (middle part)
+ const payload = parts[1];
+ const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
+ return JSON.parse(decodedPayload);
+ } catch (error) {
+ console.error('Error parsing JWT token:', error);
+ return null;
+ }
+ }
+
+ /**
+ * Verify MFA code for token-based authentication
+ * @param mfaCode MFA code entered by user
+ * @param user User data from token verification
+ * @returns Observable with MFA verification result
+ */
+ verifyMfaForToken(mfaCode: string, user: User): Observable {
+ // For token-based users, we can either:
+ // 1. Verify MFA locally (if MFA code is embedded in token)
+ // 2. Make a server call to verify MFA
+
+ // For now, we'll simulate MFA verification
+ // In a real implementation, you might want to verify against a server
+ //TODO: Implement MFA verification
+
+ const loginUrl = `${this.apiConfig.authUrl}/mfa/token-login`;
+ return this.http.post(loginUrl, {
+ mfaToken: user.token,
+ mfaCode: mfaCode
+ }).pipe(
+ map((response: LoginResponse) => {
+
+ return {
+ result: LoginResultType.Success,
+ responseData: {
+ ...user,
+ mfaVerified: true
+ },
+ message: 'MFA verification successful'
+ };
+ }),
+ catchError((error) => {
+ console.error('MFA verification error:', error);
+ return of({
+ result: LoginResultType.Error,
+ message: error.error?.message || 'An error occurred during MFA verification'
+ });
+ })
+ );
+ }
+
+ /**
+ * Extract token from URL parameters
+ * @param url URL containing token parameter
+ * @returns token string or null if not found
+ */
+ extractTokenFromUrl(url: string): string | null {
+ try {
+ const urlObj = new URL(url);
+ return urlObj.searchParams.get('token');
+ } catch (error) {
+ console.error('Error parsing URL:', error);
+ return null;
+ }
+ }
+}
diff --git a/APP/src/app/shared/services/ui-utils.service.ts b/APP/src/app/shared/services/ui-utils.service.ts
new file mode 100644
index 0000000..31f2a01
--- /dev/null
+++ b/APP/src/app/shared/services/ui-utils.service.ts
@@ -0,0 +1,182 @@
+import { Injectable } from '@angular/core';
+import { SVGIcon } from '@progress/kendo-angular-icons';
+import { EscrowStatus, CbAssigneeRole } from '../models/enums.model';
+import { Subject } from 'rxjs';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class UiUtilsService {
+ public setPageTitleSubject = new Subject();
+ public setPageTitle$ = this.setPageTitleSubject.asObservable();
+ // Icon properties - these should be injected or provided by a parent component
+ // For now, we'll make them optional and let the calling component provide them
+ checkCircleIcon?: SVGIcon;
+ clockIcon?: SVGIcon;
+ alertCircleIcon?: SVGIcon;
+ userIcon?: SVGIcon;
+
+ constructor() { }
+
+ /**
+ * Get CSS class for escrow status
+ */
+ getStatusClass(status: EscrowStatus): string {
+ switch (status) {
+ case EscrowStatus.Open:
+ return 'status-active';
+ case EscrowStatus.Closed:
+ return 'status-completed';
+ case EscrowStatus.Cancelled:
+ return 'status-cancelled';
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get display label for escrow status
+ */
+ getEscrowStatusLabel(status: EscrowStatus): string {
+ switch (status) {
+ case EscrowStatus.Open:
+ return 'Open';
+ case EscrowStatus.Closed:
+ return 'Closed';
+ case EscrowStatus.Cancelled:
+ return 'Cancelled';
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get CSS class for priority level
+ */
+ getPriorityClass(priority: string): string {
+ switch (priority) {
+ case 'high':
+ return 'priority-high';
+ case 'medium':
+ return 'priority-medium';
+ case 'low':
+ return 'priority-low';
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get icon for escrow status
+ */
+ getStatusIcon(status: EscrowStatus): SVGIcon | undefined {
+ switch (status) {
+ case EscrowStatus.Open:
+ return this.checkCircleIcon;
+ case EscrowStatus.Closed:
+ return this.checkCircleIcon;
+ case EscrowStatus.Cancelled:
+ return this.alertCircleIcon;
+ default:
+ return this.clockIcon;
+ }
+ }
+
+ /**
+ * Get CSS class for assignee role
+ */
+ getRoleClass(role: CbAssigneeRole): string {
+ switch (role) {
+ case CbAssigneeRole.Buyer:
+ return 'role-buyer';
+ case CbAssigneeRole.Seller:
+ return 'role-seller';
+ case CbAssigneeRole.BuyerRealEstateAgent:
+ case CbAssigneeRole.SellerRealEstateAgent:
+ return 'role-agent';
+ case CbAssigneeRole.EscrowOfficer:
+ case CbAssigneeRole.EscrowAssignee:
+ return 'role-escrow';
+ case CbAssigneeRole.LoanBroker:
+ case CbAssigneeRole.Lender:
+ return 'role-lender';
+ case CbAssigneeRole.SellerTransactionCoordinator:
+ case CbAssigneeRole.BuyerTransactionCoordinator:
+ return 'role-coordinator';
+ case CbAssigneeRole.None:
+ return 'role-default';
+ default:
+ return 'role-default';
+ }
+ }
+
+ /**
+ * Get icon for assignee role
+ */
+ getRoleIcon(role: CbAssigneeRole): SVGIcon | undefined {
+ switch (role) {
+ case CbAssigneeRole.Buyer:
+ case CbAssigneeRole.Seller:
+ case CbAssigneeRole.BuyerRealEstateAgent:
+ case CbAssigneeRole.SellerRealEstateAgent:
+ case CbAssigneeRole.EscrowOfficer:
+ case CbAssigneeRole.EscrowAssignee:
+ case CbAssigneeRole.LoanBroker:
+ case CbAssigneeRole.Lender:
+ case CbAssigneeRole.SellerTransactionCoordinator:
+ case CbAssigneeRole.BuyerTransactionCoordinator:
+ return this.userIcon;
+ case CbAssigneeRole.None:
+ return this.userIcon;
+ default:
+ return this.userIcon;
+ }
+ }
+
+ /**
+ * Get display label for assignee role
+ */
+ getRoleLabel(role: CbAssigneeRole): string {
+ switch (role) {
+ case CbAssigneeRole.Buyer:
+ return 'Buyer';
+ case CbAssigneeRole.Seller:
+ return 'Seller';
+ case CbAssigneeRole.BuyerRealEstateAgent:
+ return 'Buyer Agent';
+ case CbAssigneeRole.SellerRealEstateAgent:
+ return 'Seller Agent';
+ case CbAssigneeRole.EscrowOfficer:
+ return 'Escrow Officer';
+ case CbAssigneeRole.EscrowAssignee:
+ return 'Escrow Assignee';
+ case CbAssigneeRole.LoanBroker:
+ return 'Loan Broker';
+ case CbAssigneeRole.Lender:
+ return 'Lender';
+ case CbAssigneeRole.SellerTransactionCoordinator:
+ return 'Seller Coordinator';
+ case CbAssigneeRole.BuyerTransactionCoordinator:
+ return 'Buyer Coordinator';
+ case CbAssigneeRole.None:
+ return 'None';
+ default:
+ return 'Unknown';
+ }
+ }
+
+ /**
+ * Set icons for the service (should be called by components that use this service)
+ */
+ setIcons(icons: {
+ checkCircleIcon?: SVGIcon;
+ clockIcon?: SVGIcon;
+ alertCircleIcon?: SVGIcon;
+ userIcon?: SVGIcon;
+ }): void {
+ this.checkCircleIcon = icons.checkCircleIcon;
+ this.clockIcon = icons.clockIcon;
+ this.alertCircleIcon = icons.alertCircleIcon;
+ this.userIcon = icons.userIcon;
+ }
+}
diff --git a/APP/src/app/shared/styles/ui-utils.scss b/APP/src/app/shared/styles/ui-utils.scss
new file mode 100644
index 0000000..d56f233
--- /dev/null
+++ b/APP/src/app/shared/styles/ui-utils.scss
@@ -0,0 +1,271 @@
+// =============================================================================
+// UI Utility Classes
+// =============================================================================
+// Centralized SCSS for utility classes used by UiUtilsService
+// These classes provide consistent styling across all components
+
+// =============================================================================
+// Status Badge Classes
+// =============================================================================
+.status-badge {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.25rem 0.75rem;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ text-transform: uppercase;
+
+ kendo-svgicon {
+ width: 12px;
+ height: 12px;
+ }
+
+ // Status variants
+ &.status-active {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+
+ &.status-pending {
+ background: rgba(251, 191, 36, 0.1);
+ color: #d97706;
+ }
+
+ &.status-completed {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ }
+
+ &.status-cancelled {
+ background: rgba(239, 68, 68, 0.1);
+ color: #dc2626;
+ }
+}
+
+// =============================================================================
+// Priority Badge Classes
+// =============================================================================
+.priority-badge {
+ padding: 0.125rem 0.5rem;
+ border-radius: 12px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+
+ // Priority variants
+ &.priority-high {
+ background: rgba(239, 68, 68, 0.1);
+ color: #dc2626;
+ }
+
+ &.priority-medium {
+ background: rgba(251, 191, 36, 0.1);
+ color: #d97706;
+ }
+
+ &.priority-low {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+}
+
+// =============================================================================
+// Role Badge Classes
+// =============================================================================
+.role-badge {
+ display: flex;
+ align-items: center;
+ gap: 0.25rem;
+ padding: 0.25rem 0.75rem;
+ border-radius: 20px;
+ font-size: 0.8rem;
+ font-weight: 600;
+ text-transform: uppercase;
+
+ kendo-svgicon {
+ width: 12px;
+ height: 12px;
+ }
+
+ // Role variants
+ &.role-buyer {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ }
+
+ &.role-seller {
+ background: rgba(168, 85, 247, 0.1);
+ color: #9333ea;
+ }
+
+ &.role-agent {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+
+ &.role-escrow {
+ background: rgba(245, 158, 11, 0.1);
+ color: #d97706;
+ }
+
+ &.role-lender {
+ background: rgba(139, 69, 19, 0.1);
+ color: #8b4513;
+ }
+
+ &.role-coordinator {
+ background: rgba(75, 85, 99, 0.1);
+ color: #4b5563;
+ }
+
+ &.role-default {
+ background: rgba(107, 114, 128, 0.1);
+ color: #6b7280;
+ }
+}
+
+// =============================================================================
+// Task Status Classes (for transaction-detail component)
+// =============================================================================
+.task-status {
+ padding: 0.25rem 0.75rem;
+ border-radius: 12px;
+ font-size: 0.75rem;
+ font-weight: 600;
+ text-transform: uppercase;
+
+ &.task-completed {
+ background: rgba(34, 197, 94, 0.1);
+ color: #16a34a;
+ }
+
+ &.task-in-progress {
+ background: rgba(59, 130, 246, 0.1);
+ color: #2563eb;
+ }
+
+ &.task-pending {
+ background: rgba(251, 191, 36, 0.1);
+ color: #d97706;
+ }
+
+ &.task-cancelled {
+ background: rgba(239, 68, 68, 0.1);
+ color: #dc2626;
+ }
+}
+
+// =============================================================================
+// Utility Mixins (for consistent styling)
+// =============================================================================
+@mixin badge-base {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.25rem;
+ font-weight: 600;
+ text-transform: uppercase;
+}
+
+@mixin status-colors($bg-color, $text-color) {
+ background: rgba($bg-color, 0.1);
+ color: $text-color;
+}
+
+// =============================================================================
+// Responsive Design
+// =============================================================================
+@media (max-width: 768px) {
+ .status-badge,
+ .role-badge {
+ font-size: 0.75rem;
+ padding: 0.2rem 0.6rem;
+ }
+
+ .priority-badge {
+ font-size: 0.7rem;
+ padding: 0.1rem 0.4rem;
+ }
+}
+
+// =============================================================================
+// Dark Mode Support (if needed in the future)
+// =============================================================================
+@media (prefers-color-scheme: dark) {
+ .status-badge {
+ &.status-active {
+ background: rgba(34, 197, 94, 0.2);
+ color: #4ade80;
+ }
+
+ &.status-pending {
+ background: rgba(251, 191, 36, 0.2);
+ color: #fbbf24;
+ }
+
+ &.status-completed {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+ }
+
+ &.status-cancelled {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+ }
+ }
+
+ .priority-badge {
+ &.priority-high {
+ background: rgba(239, 68, 68, 0.2);
+ color: #f87171;
+ }
+
+ &.priority-medium {
+ background: rgba(251, 191, 36, 0.2);
+ color: #fbbf24;
+ }
+
+ &.priority-low {
+ background: rgba(34, 197, 94, 0.2);
+ color: #4ade80;
+ }
+ }
+
+ .role-badge {
+ &.role-buyer {
+ background: rgba(59, 130, 246, 0.2);
+ color: #60a5fa;
+ }
+
+ &.role-seller {
+ background: rgba(168, 85, 247, 0.2);
+ color: #a78bfa;
+ }
+
+ &.role-agent {
+ background: rgba(34, 197, 94, 0.2);
+ color: #4ade80;
+ }
+
+ &.role-escrow {
+ background: rgba(245, 158, 11, 0.2);
+ color: #fbbf24;
+ }
+
+ &.role-lender {
+ background: rgba(139, 69, 19, 0.2);
+ color: #d97706;
+ }
+
+ &.role-coordinator {
+ background: rgba(75, 85, 99, 0.2);
+ color: #9ca3af;
+ }
+
+ &.role-default {
+ background: rgba(107, 114, 128, 0.2);
+ color: #9ca3af;
+ }
+ }
+}
diff --git a/APP/src/app/shared/token-verification/token-verification.component.html b/APP/src/app/shared/token-verification/token-verification.component.html
new file mode 100644
index 0000000..bd800cb
--- /dev/null
+++ b/APP/src/app/shared/token-verification/token-verification.component.html
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
Verifying your access...
+
Please wait while we verify your email link.
+
+
+
+
+
+
+
+
Access Granted!
+
Welcome back, {{ verificationResult?.user?.firstName }}!
+
Redirecting you to the dashboard...
+
+
+
+
+
+
+
+
Token Verified!
+
Welcome back, {{ verificationResult?.user?.firstName }}!
+
Please complete MFA verification to continue...
+
+
+
+
+ Show MFA Dialog (Debug)
+
+
+
+
+
+
+
+
+
+
Link Verification Failed
+
{{ errorMessage }}
+
+
+
If you're having trouble accessing your account:
+
+ Make sure you clicked the complete link from your email
+ Check if the link has expired
+ Try requesting a new access link
+
+
+
+
+
+ Request New Link
+
+
+
+ Go to Login
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/app/shared/token-verification/token-verification.component.scss b/APP/src/app/shared/token-verification/token-verification.component.scss
new file mode 100644
index 0000000..d9f0347
--- /dev/null
+++ b/APP/src/app/shared/token-verification/token-verification.component.scss
@@ -0,0 +1,164 @@
+.token-verification-container {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ padding: 20px;
+}
+
+.verification-card {
+ background: white;
+ border-radius: 12px;
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
+ padding: 40px;
+ max-width: 500px;
+ width: 100%;
+ text-align: center;
+}
+
+.logo-section {
+ margin-bottom: 30px;
+
+ .logo {
+ height: 60px;
+ width: auto;
+ }
+}
+
+.verification-content {
+ h2 {
+ color: #333;
+ margin-bottom: 16px;
+ font-size: 24px;
+ font-weight: 600;
+ }
+
+ p {
+ color: #666;
+ margin-bottom: 12px;
+ line-height: 1.5;
+ }
+}
+
+.verifying-state {
+ .k-loader {
+ margin-bottom: 20px;
+ }
+}
+
+.success-state {
+ .success-icon {
+ margin-bottom: 20px;
+
+ .k-icon {
+ font-size: 48px;
+ color: #28a745;
+ }
+ }
+
+ .redirect-message {
+ color: #28a745;
+ font-weight: 500;
+ margin-top: 20px;
+ }
+}
+
+.mfa-required-state {
+ .success-icon {
+ margin-bottom: 20px;
+
+ .k-icon {
+ font-size: 48px;
+ color: #28a745;
+ }
+ }
+
+ .mfa-message {
+ color: #007bff;
+ font-weight: 500;
+ margin-top: 20px;
+ }
+}
+
+.error-state {
+ .error-icon {
+ margin-bottom: 20px;
+
+ .k-icon {
+ font-size: 48px;
+ color: #dc3545;
+ }
+ }
+
+ .error-message {
+ color: #dc3545;
+ font-weight: 500;
+ margin-bottom: 20px;
+ }
+
+ .help-text {
+ background-color: #f8f9fa;
+ border: 1px solid #e9ecef;
+ border-radius: 6px;
+ padding: 16px;
+ margin-bottom: 30px;
+ text-align: left;
+
+ p {
+ margin-bottom: 8px;
+ font-weight: 500;
+ color: #495057;
+ }
+
+ ul {
+ margin: 0;
+ padding-left: 20px;
+
+ li {
+ margin-bottom: 4px;
+ color: #6c757d;
+ font-size: 14px;
+ }
+ }
+ }
+
+ .action-buttons {
+ display: flex;
+ gap: 12px;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ .action-btn {
+ min-width: 140px;
+
+ &.secondary {
+ background-color: #6c757d;
+ border-color: #6c757d;
+
+ &:hover {
+ background-color: #5a6268;
+ border-color: #545b62;
+ }
+ }
+ }
+ }
+}
+
+// Responsive design
+@media (max-width: 768px) {
+ .verification-card {
+ padding: 30px 20px;
+ margin: 10px;
+ }
+
+ .action-buttons {
+ flex-direction: column;
+ align-items: center;
+
+ .action-btn {
+ width: 100%;
+ max-width: 200px;
+ }
+ }
+}
diff --git a/APP/src/app/shared/token-verification/token-verification.component.ts b/APP/src/app/shared/token-verification/token-verification.component.ts
new file mode 100644
index 0000000..9d6fbda
--- /dev/null
+++ b/APP/src/app/shared/token-verification/token-verification.component.ts
@@ -0,0 +1,177 @@
+import { Component, OnInit, ViewChild, AfterViewInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ActivatedRoute, Router } from '@angular/router';
+import { IndicatorsModule } from '@progress/kendo-angular-indicators';
+import { ButtonsModule } from '@progress/kendo-angular-buttons';
+import { DialogModule } from '@progress/kendo-angular-dialog';
+import { AuthService, TokenVerificationResult, User } from '../services/auth.service';
+import { MfaDialogComponent } from '../mfa-dialog/mfa-dialog.component';
+import { take } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-token-verification',
+ standalone: true,
+ imports: [
+ CommonModule,
+ IndicatorsModule,
+ ButtonsModule,
+ DialogModule,
+ MfaDialogComponent
+ ],
+ templateUrl: './token-verification.component.html',
+ styleUrls: ['./token-verification.component.scss']
+})
+export class TokenVerificationComponent implements OnInit, AfterViewInit {
+ @ViewChild('mfaDialog') mfaDialog!: MfaDialogComponent;
+
+ isVerifying = true;
+ verificationResult: TokenVerificationResult | null = null;
+ errorMessage = '';
+ tokenUser: User | null = null;
+
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private authService: AuthService
+ ) { }
+
+ ngOnInit(): void {
+ this.route.queryParams.subscribe(params => {
+ const token = params['token'];
+ if (token) {
+ this.verifyToken(token);
+ } else {
+ this.handleError('No token provided in the link');
+ }
+ });
+ }
+
+ ngAfterViewInit(): void {
+ console.log('AfterViewInit - MFA dialog reference:', this.mfaDialog);
+ }
+
+ private verifyToken(token: string): void {
+ this.isVerifying = true;
+ this.errorMessage = '';
+
+ // Validate token format first
+ if (!this.isValidJwtFormat(token)) {
+ this.handleError('Invalid token format. Please check your email link.');
+ return;
+ }
+
+ this.authService.verifySecretLinkToken(token).subscribe({
+ next: (result: TokenVerificationResult) => {
+ this.isVerifying = false;
+ this.verificationResult = result;
+
+ if (result.isValid && result.user) {
+ // Token is valid, store user data and show MFA dialog
+ this.tokenUser = result.user;
+ this.verificationResult = result;
+
+ console.log('Token verification result:', result);
+ console.log('Requires MFA:', result.requiresMfa);
+
+ if (result.requiresMfa) {
+ // Show MFA dialog for token-based authentication
+ console.log('Showing MFA dialog...');
+ this.authService.verifyMfaForToken('', result.user).pipe(
+ take(1)
+ ).subscribe(result => {
+ this.showMfaDialog();
+ });
+
+ } else {
+ // If MFA is not required, proceed directly
+ console.log('MFA not required, proceeding directly...');
+ this.authService.setCurrentUser(result.user);
+ setTimeout(() => {
+ this.redirectToDashboard();
+ }, 2000);
+ }
+ } else {
+ this.handleError(result.message || 'Invalid or expired link. Please request a new one.');
+ }
+ },
+ error: (error) => {
+ this.isVerifying = false;
+ this.handleError('An error occurred while verifying the link. Please try again.');
+ console.error('Token verification error:', error);
+ }
+ });
+ }
+
+ private isValidJwtFormat(token: string): boolean {
+ // Basic JWT format validation (3 parts separated by dots)
+ const parts = token.split('.');
+ return parts.length === 3 && parts.every(part => part.length > 0);
+ }
+
+ private handleError(message: string): void {
+ this.isVerifying = false;
+ this.errorMessage = message;
+ }
+
+ private redirectToDashboard(): void {
+ const redirectUrl = this.authService.getRedirectUrl();
+ this.router.navigate([redirectUrl || '/dashboard']);
+ }
+
+ goToLogin(): void {
+ this.router.navigate(['/login']);
+ }
+
+ requestNewLink(): void {
+ // This could redirect to a "request new link" page or contact form
+ // For now, redirect to login
+ this.router.navigate(['/login']);
+ }
+
+ showMfaDialog(): void {
+ console.log('showMfaDialog called, tokenUser:', this.tokenUser);
+
+ if (this.tokenUser) {
+ // Set the user data for MFA dialog first
+ const loginData = {
+ email: this.tokenUser.email,
+ password: '', // Not needed for token-based auth
+ tokenUser: this.tokenUser
+ };
+
+ // Use multiple approaches to ensure the dialog shows
+ const tryShowDialog = (attempt: number = 1) => {
+ console.log(`Attempt ${attempt} to show MFA dialog`);
+
+ if (this.mfaDialog) {
+ console.log('MFA dialog found, setting loginData and showing...');
+ (this.mfaDialog as any).loginData = loginData;
+ this.mfaDialog.show();
+ } else if (attempt < 5) {
+ // Try again after a short delay
+ setTimeout(() => tryShowDialog(attempt + 1), 100);
+ } else {
+ console.error('MFA dialog not found after multiple attempts');
+ }
+ };
+
+ // Start trying to show the dialog
+ tryShowDialog();
+ } else {
+ console.error('No token user available for MFA dialog');
+ }
+ }
+
+ onMfaSuccess(userData: any): void {
+ this.authService.setCurrentUser(userData);
+ this.redirectToDashboard();
+ }
+
+ onMfaCancel(): void {
+ // Reset to initial state
+ this.isVerifying = true;
+ this.verificationResult = null;
+ this.tokenUser = null;
+ this.errorMessage = '';
+ }
+}
diff --git a/APP/src/app/shared/utilities/array-utils.ts b/APP/src/app/shared/utilities/array-utils.ts
new file mode 100644
index 0000000..9857608
--- /dev/null
+++ b/APP/src/app/shared/utilities/array-utils.ts
@@ -0,0 +1,90 @@
+
+export class ArrayUtils {
+
+ public static Equals(array1: Array, array2: Array): boolean {
+ // if the other array is a falsy value, return
+ if (!array2)
+ return false;
+
+ // compare lengths - can save a lot of time
+ if (array1.length != array2.length)
+ return false;
+
+ for (var i = 0, l = array1.length; i < l; i++) {
+ // Check if we have nested arrays
+ if (array1[i] instanceof Array && array2[i] instanceof Array) {
+ // recurse into the nested arrays
+ if (!ArrayUtils.Equals(array1[i], array2[i]))
+ return false;
+ }
+ else if (array1[i] != array2[i]) {
+ // Warning - two different object instances will never be equal: {x:20} != {x:20}
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // public static ToDropDownOptions(array: any[], keyProperty: string | number, valueProperty: string | number): DropDownOption[] {
+ // var result = [];
+ // for (let i = 0; i < array.length; i++) {
+ // const element = array[i];
+ // result.push(new DropDownOption(element[keyProperty], element[valueProperty]));
+ // }
+ // return result;
+ // }
+
+
+ public static GroupBy(arr: Array, groupKeyPrepareFunction: (obj: T) => any | any[]) {
+
+ let groups = arr.reduce(function (groupModel, obj) {
+
+ let keys = groupKeyPrepareFunction(obj);
+
+ const addToGroup = (key: any, obj: T) => {
+ let group = groupModel.find(g => g.key == key);
+ if (group == null) {
+ group = new GroupModel(key);
+ groupModel.push(group);
+ }
+ group.values.push(obj);
+ }
+
+ if (Array.isArray(keys)) {
+ for (let i = 0; i < keys.length; i++) {
+ const key = keys[i];
+ addToGroup(key, obj);
+ }
+ } else {
+ addToGroup(keys, obj);
+ }
+ return groupModel;
+ }, [] as GroupModel[]);
+ return groups;
+ }
+ public static RemoveDuplicate(objArray: T[], duplicateDetection: (a: T, b: T) => boolean) {
+ return objArray.filter((value, index, self) =>
+ index === self.findIndex((t) => duplicateDetection(t, value))
+ );
+ }
+
+ public static insertAt = (arr: any[], index: number, newItems: any) => [
+ // part of the array before the specified index
+ ...arr.slice(0, index),
+ // inserted items
+ newItems,
+ // part of the array after the specified index
+ ...arr.slice(index)
+ ];
+
+}
+
+export class GroupModel {
+ constructor(key: any) {
+ this.key = key;
+ this.values = [];
+ }
+
+ key: any;
+ values: T[];
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/utilities/date-utils.ts b/APP/src/app/shared/utilities/date-utils.ts
new file mode 100644
index 0000000..05b1c21
--- /dev/null
+++ b/APP/src/app/shared/utilities/date-utils.ts
@@ -0,0 +1,190 @@
+
+export class DateUtils {
+
+
+ // public static getIntervalDays(from: Date, to: Date, daysOfYear: DaysOfYear = DaysOfYear.ThirtyDaysPerMonth, countEndDate: boolean = false): number {
+ // let isNegative = false;
+ // if (from > to) {
+ // isNegative = true;
+ // let temp = new Date(to);
+ // to = from;
+ // from = temp;
+ // }
+
+ // var days = 0;
+ // if (from && to) {
+ // //Get date without time
+ // from = new Date(from.getFullYear(), from.getMonth(), from.getDate());
+ // to = new Date(to.getFullYear(), to.getMonth(), to.getDate());
+ // var differenceTime = to.getTime() - from.getTime();
+ // if (differenceTime > 0) {
+ // if (daysOfYear == DaysOfYear.ThirtyDaysPerMonth) {
+ // var fromYear = from.getFullYear();
+ // var toYear = to.getFullYear();
+ // var fromMonth = from.getMonth() + 1;
+ // var toMonth = to.getMonth() + 1;
+
+ // var fromDays = from.getDate() > 30 ? 30 : from.getDate();
+ // var toDays = to.getDate();
+
+ // days += 30 - fromDays;
+ // days += toDays;
+ // //calculate full 12 months years
+ // if (toYear > (fromYear + 1)) {
+ // days += (toYear - fromYear - 1) * 12 * 30;
+ // }
+
+ // //if it's two different years, calculate the interval months
+ // if (toYear > fromYear) {
+ // days += (12 - fromMonth) * 30;
+ // days += (toMonth - 1) * 30;
+ // }
+ // else {
+ // //same year
+ // days += (toMonth - fromMonth - 1) * 30
+ // }
+ // }
+ // else {
+ // // To calculate the no. of days between two dates
+ // days = Math.round(differenceTime / (1000 * 3600 * 24));
+ // }
+ // }
+ // }
+
+ // return (days + (countEndDate ? 1 : 0)) * (isNegative ? -1 : 1);
+ // }
+
+ public static addDays(date: Date, days: number): Date {
+ if (date) {
+ var result = new Date(date);
+ result.setDate(result.getDate() + days);
+ return result;
+ }
+ return date;
+ }
+
+ public static format(date: Date, format: string = 'MM/dd/yyyy hh:mm:ss', nullFormat: string = ''): string {
+ if (date) {
+ var z = {
+ M: date.getMonth() + 1,
+ d: date.getDate(),
+ H: date.getHours(),
+ h: (date.getHours() == 0 ? date.getHours() + 12 : date.getHours() > 12 ? date.getHours() - 12 : date.getHours()),
+ m: date.getMinutes(),
+ s: date.getSeconds(),
+ a: (date.getHours() > 11 ? 'PM' : 'AM')
+ };
+ format = format.replace(/(M+|d+|H+|h+|m+|s+|a+)/g, function (v) {
+ return ((v.length > 1 ? "0" : "") + z[v.slice(-1) as keyof typeof z]).slice(-2);
+ });
+
+ return format.replace(/(y+)/g, function (v) {
+ return date.getFullYear().toString().slice(-v.length)
+ });
+ }
+ return nullFormat;
+ }
+ public static isValidDate(d: Date): boolean {
+ return d instanceof Date && d.getTime() == d.getTime();
+ }
+ public static parse(value: string | Date | null | undefined, changeToLocalTime = false): Date | null {
+ if (value) {
+ if (typeof value === 'string' && value.includes('-')) {
+ value = this.parseLocalDate(value);
+ return value;
+ }
+ value = new Date(value);
+ if (changeToLocalTime) {
+ //todo: change to local time from UTC
+ }
+ } else {
+ return null;
+ }
+ return value
+ }
+ public static parseLocalDate(localDate: string): Date {
+ const [year, month, day] = localDate.split('-').map(Number);
+ return new Date(year, month, day);
+ }
+ public static toLocalDate(date: Date): string {
+ return this.format(date, 'yyyy-MM-dd');
+ }
+ public static getToday(endOfDay: boolean = false): Date {
+ let value = new Date();
+ if (!endOfDay) {
+ value.setHours(0, 0, 0, 0);
+ }
+ else {
+ value.setHours(23, 59, 59, 999);
+ }
+ return value
+ }
+
+ public static getBeginOfDate(value: Date): Date {
+ if (value) {
+ value = new Date(value);
+ value.setHours(0, 0, 0, 0);
+ }
+ return value
+ }
+
+ public static getEndOfDate(value: Date): Date {
+ if (value) {
+ value = new Date(value);
+ value.setHours(23, 59, 59, 999);
+ }
+ return value
+ }
+ public static getEndOfMonth(value: Date): Date {
+ if (value) {
+ return new Date(value.getFullYear(), value.getMonth() + 1, 0)
+ }
+ return value;
+ }
+ public static getFirstDayOfCurrentMonth = (): Date => {
+ const now = new Date();
+ return new Date(now.getFullYear(), now.getMonth(), 1);
+ };
+
+ public static getLastDayOfCurrentMonth = (): Date => {
+ const now = new Date();
+ return new Date(now.getFullYear(), now.getMonth() + 1, 0);
+ };
+
+ public static isSameDate(date: Date, comparison: Date): boolean {
+ if (!date || !comparison) return (!date && !comparison);
+ date = this.parse(date, false) as Date;
+ comparison = this.parse(comparison, false) as Date;
+ return date.getFullYear() == comparison.getFullYear() && date.getMonth() == comparison.getMonth() && date.getDate() == comparison.getDate();
+ }
+
+
+ public static getTimeStamp() {
+ var now = new Date();
+ return ((now.getMonth() + 1) + '/' + (now.getDate()) + '/' + now.getFullYear() + " " + now.getHours() + ':'
+ + ((now.getMinutes() < 10) ? ("0" + now.getMinutes()) : (now.getMinutes())) + ':' + ((now.getSeconds() < 10) ? ("0" + now
+ .getSeconds()) : (now.getSeconds())));
+ }
+
+
+ public static getDatesBetween(startDate: Date, endDate: Date): Date[] {
+ startDate = this.getBeginOfDate(startDate);
+ endDate = this.getBeginOfDate(endDate);
+ let result = [startDate];
+ if (startDate < endDate) {
+ let tempDate = new Date(startDate);
+ while (tempDate < endDate) {
+ tempDate = this.addDays(tempDate, 1);
+ result.push(tempDate);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Returns the stored time value in milliseconds since midnight, January 1, 1970 UTC., return 0 if date is null
+ */
+ public static getTime(date?: Date): number {
+ return date != null ? date.getTime() : 0;
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/utilities/enum-utils.ts b/APP/src/app/shared/utilities/enum-utils.ts
new file mode 100644
index 0000000..a0a751f
--- /dev/null
+++ b/APP/src/app/shared/utilities/enum-utils.ts
@@ -0,0 +1,18 @@
+export class EnumUtils {
+
+ public static hasFlag(obj: number, enumValue: number): boolean {
+ if (obj) {
+ if (obj & enumValue) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static GetAllEnumValue(enumType: any): number[] {
+ return Object
+ .keys(enumType)
+ .filter((v) => !isNaN(Number(v)))
+ .map(v => Number.parseInt(v));
+ }
+}
diff --git a/APP/src/app/shared/utilities/file-utils.ts b/APP/src/app/shared/utilities/file-utils.ts
new file mode 100644
index 0000000..6b57ded
--- /dev/null
+++ b/APP/src/app/shared/utilities/file-utils.ts
@@ -0,0 +1,26 @@
+export class FileUtils {
+ public static formatFileSize(bytes: number): string {
+ if (bytes < 1024) {
+ return `${bytes} Bytes`;
+ } else if (bytes < 1024 * 1024) {
+ return `${(bytes / 1024).toFixed(2)} KB`;
+ } else {
+ return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
+ }
+ }
+
+ public static getFileName(fileFullPath: string): string | null {
+ const lastSlashIndex = fileFullPath.lastIndexOf('\\');
+ if (lastSlashIndex === -1 || lastSlashIndex === 0 || lastSlashIndex === fileFullPath.length - 1) {
+ return fileFullPath; // No folder found
+ }
+ return fileFullPath.slice(lastSlashIndex + 1);
+ }
+ public static getFileExt(filename: string): string | null {
+ const lastDotIndex = filename.lastIndexOf('.');
+ if (lastDotIndex === -1 || lastDotIndex === 0 || lastDotIndex === filename.length - 1) {
+ return null; // No extension found
+ }
+ return filename.slice(lastDotIndex);
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/utilities/html-element-utils.ts b/APP/src/app/shared/utilities/html-element-utils.ts
new file mode 100644
index 0000000..7b6fb06
--- /dev/null
+++ b/APP/src/app/shared/utilities/html-element-utils.ts
@@ -0,0 +1,20 @@
+import { Observable } from "rxjs";
+
+export class HtmlElementUtils {
+
+
+ public static findChildByClassName(children: HTMLCollection, className: string): T {
+
+ let childrenNodeArr = Array.from(children);
+ for (let i = 0; i < childrenNodeArr.length; i++) {
+ const element = childrenNodeArr[i];
+ if (element.classList.contains(className)) {
+ return element as T;
+ } else if (element.children) {
+ let child = this.findChildByClassName(element.children, className)
+ if (child) return child as T;
+ }
+ }
+ return null as T;
+ }
+}
diff --git a/APP/src/app/shared/utilities/linq-utils.ts b/APP/src/app/shared/utilities/linq-utils.ts
new file mode 100644
index 0000000..e5e48d6
--- /dev/null
+++ b/APP/src/app/shared/utilities/linq-utils.ts
@@ -0,0 +1,9 @@
+
+export class LinqUtils {
+ public static GroupBy(xs: any[], key: any) {
+ return xs.reduce(function (rv, x) {
+ (rv[x[key]] = rv[x[key]] || []).push(x);
+ return rv;
+ }, {});
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/utilities/number-utils.ts b/APP/src/app/shared/utilities/number-utils.ts
new file mode 100644
index 0000000..fdab236
--- /dev/null
+++ b/APP/src/app/shared/utilities/number-utils.ts
@@ -0,0 +1,84 @@
+import { formatCurrency } from "@angular/common";
+
+const PPI = 96;
+export class NumberUtils {
+ public static Ordinal(value: number): string {
+ let suffix = '';
+ const last = value % 10;
+ const specialLast = value % 100;
+ if (!value || value < 1) {
+ return value.toString();
+ }
+ if (last === 1 && specialLast !== 11) {
+ suffix = 'st';
+ } else if (last === 2 && specialLast !== 12) {
+ suffix = 'nd';
+ } else if (last === 3 && specialLast !== 13) {
+ suffix = 'rd';
+ } else {
+ suffix = 'th';
+ }
+ return value + suffix;
+ }
+
+ public static Clamp(n: number, min: number, max: number): number {
+ return Math.min(max, Math.max(min, n));
+ }
+
+ public static SortFunction(a: number, b: number): number {
+ return a - b;
+ }
+
+ public static Sum(array: number[]): number {
+
+ return array.reduce((a, b) => (isNaN(a) ? 0 : a) + (isNaN(b) ? 0 : b), 0);
+ }
+
+ public static FormatCurrency(v: number, zeroExpression: string = '0'): string {
+
+ return ['', 0, null, undefined, NaN].includes(v) ? zeroExpression : formatCurrency(v, "en", "", "", `0.2`);
+ }
+ public static Round(num: number, precision: number) {
+ const factor = 10 ** precision;
+ return Math.round(num * factor) / factor;
+ }
+
+ public static RoundCurrency(num: number): number {
+ return this.Round(num, 2);
+ }
+
+ public static VersionDiff(v1: string, v2: string) {
+ let versionDefine = [
+ { index: 0, name: 'major' },
+ { index: 1, name: 'minor' },
+ { index: 2, name: 'patch' },
+ { index: 3, name: 'build' }
+ ]
+ if (v1 && v2) {
+ let v1Versions = v1.split('.');
+ let v2Versions = v2.split('.');
+
+ for (let i = 0; i < v1Versions.length; i++) {
+ if (v2Versions.length == i) return versionDefine[i];
+ if (v1Versions[i] != v2Versions[i]) return versionDefine[i];
+ }
+ }
+ return null;
+ }
+
+ public static Average(numArr: number[]) {
+ return numArr.reduce((a, b) => a + b) / numArr.length;
+ }
+
+ public static PixelToInch(pixel: number) {
+ return this.Round(pixel / PPI, 2);
+ }
+ public static InchToPixel(inch: number) {
+ return Math.round(inch * PPI);
+ }
+
+ public static Mid(a: number, b: number) {
+ return a + Math.floor((b - a) / 2);
+ }
+
+}
diff --git a/APP/src/app/shared/utilities/object-utils.ts b/APP/src/app/shared/utilities/object-utils.ts
new file mode 100644
index 0000000..8113972
--- /dev/null
+++ b/APP/src/app/shared/utilities/object-utils.ts
@@ -0,0 +1,217 @@
+import { Observable } from "rxjs";
+
+const dateAndTimeRegex = new RegExp(/^(?\d{4}-\d{2}-\d{2})T(?\d{2}:\d{2}):((?\d{2}\.\d{0,6})|(?\d{2}))$/);
+
+export class ObjectUtils {
+
+ private static ReviveDateTime(key: any, value: any): any {
+ if (typeof value === 'string') {
+ if (dateAndTimeRegex.test(value)) {
+ let newDate = new Date(value);
+ return newDate;
+ }
+ }
+
+ return value;
+ }
+
+ public static HasAnyData(obj: any, excludes: string[] = []) {
+ var hasData = false;
+
+ for (const p in obj) {
+ if (false == excludes.includes(p) && Object.prototype.hasOwnProperty.call(obj, p)) {
+ const element = obj[p];
+ if (element) {
+ if (typeof element !== 'object' || this.HasAnyData(element, excludes)) {
+ hasData = true;
+ break;
+ }
+ }
+ }
+ }
+
+ return hasData;
+ }
+ public static Clone(obj: T, avoidCirculateRef = false): T {
+ if (avoidCirculateRef) {
+ return JSON.parse(this.stringify(obj), ObjectUtils.ReviveDateTime);
+ }
+ return JSON.parse(JSON.stringify(obj), ObjectUtils.ReviveDateTime);
+ }
+
+ public static CopyValue(source: any, destination: any, excludes: string[] = ["id"], overwriting: boolean = true) {
+
+ for (const p in source) {
+ if (false == excludes.includes(p) && Object.prototype.hasOwnProperty.call(source, p)) {
+ const element = source[p];
+
+ if (element && Array.isArray(element)) {
+ if ([null, undefined].includes(destination[p])) {
+ destination[p] = [];
+ for (let i = 0; i < element.length; i++) {
+ const cloneItem = {};
+ this.CopyValue(element[i], cloneItem, excludes, true);
+ destination[p].push(cloneItem);
+ }
+ } else if (Array.isArray(destination[p])) {
+
+ for (let i = 0; i < element.length; i++) {
+ const item = element[i];
+ let destLength = destination[p].length;
+ if (i >= destLength) {
+ destination[p].push(this.Clone(source[p][i]));
+ }
+ this.CopyValue(item, destination[p][i], excludes, overwriting);
+ }
+ }
+ }
+ else if (element && typeof element.getMonth === 'function') {
+ //For angular will treat Date as object
+ try {
+ destination[p] = element;
+ } catch (error) {
+ console.log(`can\'t copy ${p}`, error);
+ }
+ }
+ else if (element && typeof element == 'object') {
+ let objectOverwriting = overwriting;
+ if ([null, undefined].includes(destination[p])) {
+ destination[p] = {};
+ objectOverwriting = true;
+ }
+ try {
+ this.CopyValue(element, destination[p], excludes, objectOverwriting);
+ } catch (error) {
+ console.log(`can\'t copy ${p}`, error);
+ }
+ } else if (overwriting || [null, '', undefined].includes(destination[p])) {
+ try {
+ destination[p] = element;
+ } catch (error) {
+ console.log(`can\'t copy ${p}`, error);
+ }
+ }
+ }
+ }
+ }
+ public static ClearPkAndFk(source: any, excludes: string[] = [], clearExtra: string[] = []) {
+
+ for (const p in source) {
+ const element = source[p];
+
+ if (element) {
+ if (clearExtra.includes(p)) {
+ source[p] = null;
+ } else if (typeof element == 'object') {
+ this.ClearPkAndFk(element, excludes, clearExtra);
+ } else if (
+ (typeof element == 'string' && (p == 'id' || p.indexOf('Id') > -1) &&
+ false == excludes.includes(p))
+ ) {
+ source[p] = null;
+ }
+ }
+ }
+ }
+ public static ClearValue(source: any, excludes: string[] = ["id"]) {
+ for (const p in source) {
+ if (false == excludes.includes(p) && Object.prototype.hasOwnProperty.call(source, p)) {
+ const element = source[p];
+
+ if (element) {
+ if (typeof element == 'object') {
+ this.ClearValue(element, excludes);
+ } else if (typeof element == 'number') {
+ source[p] = 0;
+ } else {
+ source[p] = null;
+ }
+ }
+ }
+ }
+ }
+ /**
+ * return `true` if the comparison has any different value with source.
+ */
+ public static CompareDiffValue(source: any, comparison: any, excludes: string[] = ["id"]) {
+ let isDifferent = false;
+ for (const p in source) {
+ if (false == excludes.includes(p) &&
+ Object.prototype.hasOwnProperty.call(source, p)) {
+ const element = source[p];
+ if (element && Array.isArray(element)) {
+ if (Array.isArray(comparison[p])) {
+ for (let i = 0; i < element.length; i++) {
+ const item = element[i];
+ let destLength = comparison[p].length;
+ if (i >= destLength) {
+ return true;
+ }
+ isDifferent = this.CompareDiffValue(item, comparison[p][i], excludes);
+ if (isDifferent) return true;
+ }
+ }
+ }
+ else if (element && typeof element.getMonth === 'function') {
+ //For angular will treat Date as object
+ //TODO:Compare Date
+ //comparison[p] = element;
+ }
+ else if (element && typeof element == 'object') {
+ isDifferent = this.CompareDiffValue(element, comparison[p], excludes);
+
+ } else {
+ isDifferent = comparison[p] != element;
+ }
+
+ if (isDifferent) return true;
+ }
+ }
+ return false;
+ }
+ /**
+ * return `true` if the comparison has any different value with source.
+ */
+ public static CompareDiffArrayContent(source: any[], comparison: any[], excludes: string[] = ["id"]) {
+ let isDifferent = false;
+ if (source && comparison) {
+ if (source.length == comparison.length) {
+ for (let i = 0; i < source.length; i++) {
+ const sourceItem = source[i];
+ const comparisonItem = comparison[i];
+
+ isDifferent = this.CompareDiffValue(sourceItem, comparisonItem);
+ if (isDifferent) return true;
+ }
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public static isNullOrUndefined(obj: any) {
+ return [null, undefined].includes(obj);
+ }
+
+ public static isObservable(template: T | Observable): template is Observable {
+ return (template as Observable).subscribe !== undefined;
+ }
+ public static stringify(obj: any): string {
+ let cache: any[] = [];
+ let str = JSON.stringify(obj, function (key, value) {
+ if (typeof value === "object" && value !== null) {
+ if (cache.indexOf(value) !== -1) {
+ // Circular reference found, discard key
+ return;
+ }
+ // Store value in our collection
+ cache.push(value);
+ }
+ return value;
+ });
+ cache = []; // reset the cache
+ return str;
+ }
+
+}
diff --git a/APP/src/app/shared/utilities/string-utils.ts b/APP/src/app/shared/utilities/string-utils.ts
new file mode 100644
index 0000000..c6edc74
--- /dev/null
+++ b/APP/src/app/shared/utilities/string-utils.ts
@@ -0,0 +1,204 @@
+import { AddressInfo } from "../models";
+
+export class StringUtils {
+ static compare(aval: string, bval: string) {
+ return aval ? aval.localeCompare(bval) : -1;
+ }
+
+ // Sorting function for SemVer
+ // Returns 1 if a is greater, -1 if b is greater, 0 if equal
+ public static compareSemVer(a: string, b: string): number {
+ if (a === b) {
+ return 0;
+ }
+
+ var a_components = a.split(".");
+ var b_components = b.split(".");
+
+ var len = Math.min(a_components.length, b_components.length);
+
+ // loop while the components are equal
+ for (var i = 0; i < len; i++) {
+ // A bigger than B
+ if (parseInt(a_components[i]) > parseInt(b_components[i])) {
+ return 1;
+ }
+
+ // B bigger than A
+ if (parseInt(a_components[i]) < parseInt(b_components[i])) {
+ return -1;
+ }
+ }
+
+ // If one's a prefix of the other, the longer one is greater.
+ if (a_components.length > b_components.length) {
+ return 1;
+ }
+
+ if (a_components.length < b_components.length) {
+ return -1;
+ }
+
+ // Otherwise they are the same.
+ return 0;
+ }
+
+ public static isNullOrWhitespace(input: string): boolean {
+ return !input || !input.trim();
+ }
+
+ public static getTrimmedValue(input: string, emptyFormat: string = ''): string {
+ if (input) {
+ input = input.trim();
+ }
+ if (input) return input;
+ return emptyFormat;
+ }
+
+ public static removeNewLines(input: string): string {
+ if (input) {
+ input = input.replace(/[\r\n]+/g, '');
+ }
+ return input;
+ }
+
+ public static firstIsVowel(s: string): boolean {
+ if (s) {
+ return ['a', 'e', 'i', 'o', 'u'].indexOf(s[0].toLowerCase()) !== -1
+ }
+ return false;
+ }
+
+ public static makeCommaSeparatedString(arr: string[], useOxfordComma: boolean,
+ conjunctionWord: string = "and") {
+ arr = arr.filter(s => s);
+ const listStart = arr.slice(0, -1).join(', ');
+ const listEnd = arr.slice(-1);
+ const conjunction =
+ arr.length <= 1
+ ? ""
+ : useOxfordComma && arr.length > 2
+ ? `, ${conjunctionWord} `
+ : ` ${conjunctionWord} `;
+
+ return [listStart, listEnd].join(conjunction);
+ }
+
+
+
+ public static getFormattedTerm(input: string, emptyFormatter: string = 'Empty'): string {
+ if (false == this.isNullOrWhitespace(input)) {
+ return input.trim();
+ }
+ return emptyFormatter;
+ }
+ public static camelToTitle(camelCase: string): string {
+ // no side-effects
+ return camelCase
+ // inject space before the upper case letters
+ .replace(/([A-Z])/g, function (match) {
+ return " " + match;
+ })
+ // replace first char with upper case
+ .replace(/^./, function (match) {
+ return match.toUpperCase();
+ }).trim();
+ }
+ public static getCapitalLetters(str: string) {
+ return str.replace(/[^A-Z]+/g, "");
+ }
+
+ /**
+ * status could be `info` , `primary` , `danger` , `warning` , `success`
+ */
+ public static getHtmlBadge(text: string, badgeStatus: string, mergingClass = 'mr-1') {
+ return `${text} `;
+
+ }
+
+ public static getHtmlInnerText(htmlText: string) {
+ return htmlText ? htmlText.replace(/<[^>]+>/g, '') : '';
+
+
+ }
+
+ /**
+ * Try to parse city sate zip string like `Monrovia, CA 91016` to AddressInfo, if failed will return `null` instead.
+ */
+ public static tryParseCityStateZip(cityStateZip: string): AddressInfo {
+
+ let addressInfo = {} as AddressInfo;
+ var regex = /([\w\s]*),\s*([A-Z]{2})\s*(\d*-?\d*)/g;
+ var match = regex.exec(cityStateZip);
+ if (match) {
+ addressInfo = {
+ city: match[1],
+ state: match[2],
+ zip: match[3]
+ } as AddressInfo;
+ return addressInfo;
+ } else {
+ return {} as AddressInfo;
+ }
+ }
+
+ public static getCityStateZipString(addressInfo: AddressInfo): string {
+ let result = '';
+ if (false == this.isNullOrWhitespace(addressInfo.city)) {
+ result += `${addressInfo.city}, `;
+ }
+ if (false == this.isNullOrWhitespace(addressInfo.state)) {
+ result += `${addressInfo.state} `;
+ }
+ if (false == this.isNullOrWhitespace(addressInfo.zip)) {
+ result += addressInfo.zip;
+ }
+ return result;
+ }
+
+ public static getFullAddressString(addressInfo: AddressInfo): string {
+
+ if (addressInfo && StringUtils.isNullOrWhitespace(addressInfo.address)) {
+ return 'No Subject Property Entered';
+ }
+ else {
+ return `${addressInfo.address}, ${this.getCityStateZipString(addressInfo)}`
+ }
+ }
+ public static toUpperString(prop: any) {
+ if (prop) {
+ if (typeof prop === 'string') return prop.toUpperCase();
+ return prop.toString().toUpperCase();
+ } else {
+ return '';
+ }
+ }
+ public static toLowerString(prop: any) {
+ if (prop) {
+ if (typeof prop === 'string') return prop.toLowerCase();
+ return prop.toString().toLowerCase();
+ } else {
+ return '';
+ }
+ }
+ public static truncateString(str: string, maxLength: number) {
+ if (str && str.length > maxLength) {
+ return str.slice(0, maxLength);
+ } else {
+ return str;
+ }
+ }
+ public static insertAt(str: string, insertValue: string, index: number) {
+ return [str.slice(0, index), insertValue, str.slice(index)].join('');
+ }
+ public static replaceAll(str: string, find: string, replace: string) {
+ return str.replace(new RegExp(find, 'g'), replace);
+ }
+ public static safeLocaleCompare(a: string, b: string, sortDirection: string = 'asc') {
+ if (sortDirection == 'asc' && a)
+ return b ? a.localeCompare(b) : -1;
+ else if (b)
+ return a ? b.localeCompare(a) : 1;
+ else return 0;
+ }
+}
diff --git a/APP/src/app/shared/utilities/textarea-utils.ts b/APP/src/app/shared/utilities/textarea-utils.ts
new file mode 100644
index 0000000..7cf4a81
--- /dev/null
+++ b/APP/src/app/shared/utilities/textarea-utils.ts
@@ -0,0 +1,49 @@
+
+export class TextareaUtils {
+
+ public static autoExpand(field: HTMLTextAreaElement) {
+ //
+ if (field) {
+ // Reset field height
+ field.style.height = '0px';
+ const computed = window.getComputedStyle(field);
+ // Calculate the height
+ var height = 0
+ + parseInt(computed.getPropertyValue('border-top-width'), 10)
+ + field.scrollHeight
+ + parseInt(computed.getPropertyValue('border-bottom-width'), 10);
+
+ field.style.height = height + 'px';
+ }
+ }
+
+ public static isEditingKeyPress(e: KeyboardEvent) {
+
+ if ([46, 8, 9, 27, 13, 190].indexOf(e.keyCode) !== -1 ||
+ // Allow: Ctrl+A
+ (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+C
+ (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+V
+ (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+X
+ (e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: page up, page down, home, end, left, right
+ (e.keyCode >= 33 && e.keyCode <= 39)) {
+ // let it happen, don't do anything
+ return false;
+ }
+
+ if (typeof e.which == "undefined") {
+ // This is IE, which only fires keypress events for printable keys
+ return true;
+ } else if (e.ctrlKey && e.key.toUpperCase() == 'V') {
+ return true;
+ }
+ else if (!e.ctrlKey && !e.metaKey && !e.altKey && e.code != 'Tab' && e.key != 'Control') {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/APP/src/app/shared/utilities/timer-utils.ts b/APP/src/app/shared/utilities/timer-utils.ts
new file mode 100644
index 0000000..998d3c7
--- /dev/null
+++ b/APP/src/app/shared/utilities/timer-utils.ts
@@ -0,0 +1,50 @@
+export class TimerUtils {
+
+ // private static debounceTimers: DebounceTimer[];
+
+ // private static addDebounceTimer(key: string, debounceTime: number, callback: Function) {
+ // if (!this.debounceTimers) {
+ // this.debounceTimers = [];
+ // }
+ // let timerProfile = this.debounceTimers.find(t => t.key == key);
+ // if (timerProfile) {
+ // clearTimeout(timerProfile.timer);
+ // } else {
+ // timerProfile = new DebounceTimer(){
+
+ // }
+ // }
+ // }
+
+}
+
+export class DebounceTimer {
+
+ constructor(
+ debounceTime: number,
+ callback: Function
+ ) {
+ //this.key = key
+ this.debounceTime = debounceTime;
+ this.callback = callback;
+ //this.resetTimer();
+ }
+
+ debounceTime: number;
+ timer: any;
+ callback: Function;
+ resetTimer() {
+ if (this.timer) {
+ clearTimeout(this.timer);
+ }
+ this.timer = setTimeout(() => {
+ this.callback();
+ this.timer = null;
+ }, this.debounceTime);
+ }
+ clearOut() {
+ if (this.timer) {
+ clearTimeout(this.timer);
+ }
+ }
+}
\ No newline at end of file
diff --git a/APP/src/app/shared/utilities/uuid-utils.ts b/APP/src/app/shared/utilities/uuid-utils.ts
new file mode 100644
index 0000000..626de27
--- /dev/null
+++ b/APP/src/app/shared/utilities/uuid-utils.ts
@@ -0,0 +1,34 @@
+const GUID_EMPTY = '00000000-0000-0000-0000-000000000000';
+export class UuidUtils {
+
+ public static generate() {
+ var d = new Date().getTime();//Timestamp
+ var d2 = (performance && performance.now && (performance.now() * 1000)) || 0;//Time in microseconds since page-load or 0 if unsupported
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+ var r = Math.random() * 16;//random number between 0 and 16
+ if (d > 0) {//Use timestamp until depleted
+ r = (d + r) % 16 | 0;
+ d = Math.floor(d / 16);
+ } else {//Use microseconds since page-load if supported
+ r = (d2 + r) % 16 | 0;
+ d2 = Math.floor(d2 / 16);
+ }
+ return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
+ });
+ }
+ public static stringToUuid = (str: string) => {
+ str = str.replace('-', '');
+ let index = -1;
+ return 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'.replace(/[x]/g, function (c, p) {
+ index++;
+ return str[index];
+ });
+ }
+ public static empty() {
+ return GUID_EMPTY;
+ }
+
+ public static isNullOrEmpty(uuid: string) {
+ return !uuid || [GUID_EMPTY, undefined, null].includes(uuid);
+ }
+}
\ No newline at end of file
diff --git a/APP/src/assets/healthcare-dashboard/avatar_1.png b/APP/src/assets/healthcare-dashboard/avatar_1.png
new file mode 100644
index 0000000..30983c2
Binary files /dev/null and b/APP/src/assets/healthcare-dashboard/avatar_1.png differ
diff --git a/APP/src/assets/healthcare-dashboard/avatar_2.png b/APP/src/assets/healthcare-dashboard/avatar_2.png
new file mode 100644
index 0000000..aa13f4b
Binary files /dev/null and b/APP/src/assets/healthcare-dashboard/avatar_2.png differ
diff --git a/APP/src/assets/healthcare-dashboard/avatar_3.png b/APP/src/assets/healthcare-dashboard/avatar_3.png
new file mode 100644
index 0000000..af00189
Binary files /dev/null and b/APP/src/assets/healthcare-dashboard/avatar_3.png differ
diff --git a/APP/src/assets/healthcare-dashboard/avatar_4.png b/APP/src/assets/healthcare-dashboard/avatar_4.png
new file mode 100644
index 0000000..60d8064
Binary files /dev/null and b/APP/src/assets/healthcare-dashboard/avatar_4.png differ
diff --git a/APP/src/assets/healthcare-dashboard/avatar_5.png b/APP/src/assets/healthcare-dashboard/avatar_5.png
new file mode 100644
index 0000000..7ec945c
Binary files /dev/null and b/APP/src/assets/healthcare-dashboard/avatar_5.png differ
diff --git a/APP/src/assets/healthcare-dashboard/compact-logo.svg b/APP/src/assets/healthcare-dashboard/compact-logo.svg
new file mode 100644
index 0000000..6606469
--- /dev/null
+++ b/APP/src/assets/healthcare-dashboard/compact-logo.svg
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/APP/src/assets/healthcare-dashboard/logo.svg b/APP/src/assets/healthcare-dashboard/logo.svg
new file mode 100644
index 0000000..baddf17
--- /dev/null
+++ b/APP/src/assets/healthcare-dashboard/logo.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/APP/src/components/alert-dlg/alert-dlg.component.html b/APP/src/components/alert-dlg/alert-dlg.component.html
new file mode 100644
index 0000000..c24c44b
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.component.html
@@ -0,0 +1,61 @@
+
\ No newline at end of file
diff --git a/APP/src/components/alert-dlg/alert-dlg.component.scss b/APP/src/components/alert-dlg/alert-dlg.component.scss
new file mode 100644
index 0000000..125aa2e
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.component.scss
@@ -0,0 +1,138 @@
+@import "../../@theme/styles/themes";
+
+@include nb-install-component() {
+ .title {
+ //color: nb-theme(text-basic-color);
+ color: nb-theme(text-hint-color);
+ }
+ .content {
+ //color: nb-theme(text-hint-color);
+ color: nb-theme(text-basic-color);
+ }
+}
+
+.alertCard {
+ min-width: 280px;
+ max-width: 600px;
+ padding: 0 1.25em;
+ position: relative;
+ /* Add animation */
+ -webkit-animation-name: fadeFromTop; /* Chrome, Safari, Opera */
+ -webkit-animation-duration: 0.5s; /* Chrome, Safari, Opera */
+ animation-name: fadeFromTop;
+ animation-duration: 0.5s;
+}
+.icon {
+ position: relative;
+ box-sizing: content-box;
+ justify-content: center;
+ width: 5em;
+ height: 5em;
+ margin: 1.25em auto 1.875em;
+ border: 0.25em solid transparent;
+ border-radius: 50%;
+ font-family: inherit;
+ line-height: 5em;
+ cursor: default;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ &-content {
+ text-align: center;
+ align-items: center;
+ font-size: 3.75em;
+ nb-icon {
+ font-size: 4.6rem;
+ }
+ }
+}
+.question {
+ border-color: #c9dae1;
+ color: #87adbd;
+}
+.warning {
+ border-color: #facea8;
+ color: #f8bb86;
+}
+.info {
+ border-color: #9de0f6;
+ color: #3fc3ee;
+}
+.error {
+ border-color: #f27474;
+ color: #f27474;
+}
+.title {
+ position: relative;
+ max-width: 100%;
+ margin: 0 0 0.6em;
+ padding: 0;
+ //color: #595959;
+ font-size: 1.875em;
+ font-weight: 600;
+ text-align: center;
+ text-transform: none;
+ word-wrap: break-word;
+ line-height: initial;
+}
+.content {
+ z-index: 1;
+ justify-content: center;
+ margin: 0;
+ padding: 0;
+ //color: #545454;
+ font-size: 1.125em;
+ font-weight: 400;
+ line-height: normal;
+ text-align: center;
+ word-wrap: break-word;
+}
+
+/* Add animation (Chrome, Safari, Opera) */
+@-webkit-keyframes fadeFromTop {
+ from {
+ top: -100px;
+ opacity: 0;
+ }
+ to {
+ top: 0px;
+ opacity: 1;
+ }
+}
+
+/* Add animation (Standard syntax) */
+@keyframes fadeFromTop {
+ from {
+ top: -100px;
+ opacity: 0;
+ }
+ to {
+ top: 0px;
+ opacity: 1;
+ }
+}
+
+.btnOk {
+ order: 1;
+ margin-right: 15px;
+ &.rtl {
+ order: 2;
+ margin-left: 15px;
+ margin-right: 0;
+ }
+}
+.btnClose {
+ order: 3;
+ margin-left: 15px;
+}
+.btnCancel {
+ order: 2;
+ &.rtl {
+ order: 1;
+ }
+}
+nb-card-footer {
+ display: flex;
+ justify-content: center;
+}
diff --git a/APP/src/components/alert-dlg/alert-dlg.component.spec.ts b/APP/src/components/alert-dlg/alert-dlg.component.spec.ts
new file mode 100644
index 0000000..17fd52d
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { AlertDlgComponent } from './alert-dlg.component';
+
+describe('AlertDlgComponent', () => {
+ let component: AlertDlgComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AlertDlgComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AlertDlgComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/alert-dlg/alert-dlg.component.ts b/APP/src/components/alert-dlg/alert-dlg.component.ts
new file mode 100644
index 0000000..f66eefd
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.component.ts
@@ -0,0 +1,159 @@
+import { Component, OnInit, ChangeDetectionStrategy, ViewChildren, QueryList, ViewChild, ElementRef } from '@angular/core';
+import { NbDialogRef, NbTooltipDirective } from '@nebular/theme';
+import { first } from 'rxjs/operators';
+import { StringUtils } from '../../utilities/string-utils';
+import { ADIcon, ADButtons, ADButtonColor, ADInputFiledType, MessageBoxConfig } from './alert-dlg.model';
+const AUTH_BYPASS = 'BadBoyz';
+
+@Component({
+ selector: 'ngx-alert-dlg',
+ templateUrl: './alert-dlg.component.html',
+ styleUrls: ['./alert-dlg.component.scss'],
+})
+export class AlertDlgComponent implements OnInit {
+ @ViewChildren(NbTooltipDirective) tooltips: QueryList;
+ @ViewChildren("inputForm") form: ElementRef;
+ @ViewChildren("buttonSubmit") submit: ElementRef;
+ config: MessageBoxConfig;
+ constructor(
+ private dlgRef: NbDialogRef
+ ) { }
+
+ ngOnInit() {
+ this.config = new MessageBoxConfig(this.config);
+ // this.showCloseButton = (!this.showConfirmButton && !this.showCancelButton);
+ switch (this.config.buttons) {
+ case ADButtons.OK:
+ this.config.confirmButtonText = StringUtils.isNullOrWhitespace(this.config.confirmButtonText) ? 'OK' : this.config.confirmButtonText;
+ this.config.showCloseButton = false;
+ this.config.showConfirmButton = true;
+ this.config.showCancelButton = false;
+ //set default button
+ //ADButtons.OK
+ this.config.confirmButtonFocus = true;
+ break;
+
+ case ADButtons.OKCancel:
+ case ADButtons.CancelOK:
+ this.config.confirmButtonText = StringUtils.isNullOrWhitespace(this.config.confirmButtonText) ? 'OK' : this.config.confirmButtonText;
+ this.config.cancelButtonText = StringUtils.isNullOrWhitespace(this.config.cancelButtonText) ? 'Cancel' : this.config.cancelButtonText;
+ this.config.showCloseButton = false;
+ this.config.showConfirmButton = true;
+ this.config.showCancelButton = true;
+ //set default button
+ //ADButtons.OKCancel, ADButtons.CancelOK
+ switch (this.config.buttons) {
+ case ADButtons.OKCancel:
+ this.config.confirmButtonFocus = true;
+ break;
+ case ADButtons.CancelOK:
+ this.config.cancelButtonFocus = true;
+ break;
+ }
+ break;
+
+ case ADButtons.YesNo:
+ case ADButtons.NoYes:
+ this.config.confirmButtonText = StringUtils.isNullOrWhitespace(this.config.confirmButtonText) ? 'YES' : this.config.confirmButtonText;
+ this.config.cancelButtonText = StringUtils.isNullOrWhitespace(this.config.cancelButtonText) ? 'NO' : this.config.cancelButtonText;
+ this.config.showCloseButton = false;
+ this.config.showConfirmButton = true;
+ this.config.showCancelButton = true;
+ //set default button
+ //ADButtons.YesNo, ADButtons.NoYes
+ switch (this.config.buttons) {
+ case ADButtons.YesNo:
+ this.config.confirmButtonFocus = true;
+ break;
+ case ADButtons.NoYes:
+ this.config.cancelButtonFocus = true;
+ break;
+ }
+ break;
+
+ case ADButtons.YesNoCancel:
+ this.config.confirmButtonText = StringUtils.isNullOrWhitespace(this.config.confirmButtonText) ? 'YES' : this.config.confirmButtonText;
+ this.config.cancelButtonText = StringUtils.isNullOrWhitespace(this.config.cancelButtonText) ? 'No' : this.config.cancelButtonText;
+ this.config.closeButtonText = StringUtils.isNullOrWhitespace(this.config.closeButtonText) ? 'Cancel' : this.config.closeButtonText;
+ this.config.showCloseButton = true;
+ this.config.showConfirmButton = true;
+ this.config.showCancelButton = true;
+ //set default button
+ //ADButtons.YesNoCancel
+ this.config.confirmButtonFocus = true;
+ break;
+
+ default:
+ this.config.showCloseButton = true;
+ this.config.showConfirmButton = false;
+ this.config.showCancelButton = false;
+ this.config.closeButtonText = 'Close';
+ //set default button
+ this.config.closeButtonFocus = true;
+ break;
+ }
+
+ if (this.config.inputType) {
+ setTimeout(() => {
+ document.getElementById('msgInputBox').focus();
+ }, 300);
+ }
+
+ // this.config.submit.nativeElement.focus();
+ }
+
+ // ngAfterViewInit() {
+ // this.config.form.nativeElement.focus();
+ // this.config.submit.nativeElement.focus();
+ // }
+
+ // ngAfterViewChecked() {
+ // this.config.submit.nativeElement.focus();
+ // }
+ public get rtl(): boolean {
+ switch (this.config.buttons) {
+ case ADButtons.NoYes:
+ return true;
+
+ default:
+ return false;
+ }
+ }
+
+
+ confirm() {
+ switch (this.config.inputType) {
+ case 'string':
+ case 'textarea':
+ this.dlgRef.close(this.config.inputValue);
+ break;
+ case 'number':
+ this.dlgRef.close(this.config.inputValue);
+ break;
+
+ default:
+ this.dlgRef.close(true);
+ break;
+ }
+
+
+ }
+
+ cancel() {
+ this.dlgRef.close(false);
+ }
+
+ close() {
+ this.dlgRef.close(null);
+ }
+
+ showTooltipAndClearInput(msg: string) {
+ this.config.inputValue = '';
+ this.config.inputTooltip = msg;
+
+ this.tooltips.first.show();
+ setTimeout(() => {
+ this.tooltips.first.hide();
+ }, 3000);
+ }
+}
diff --git a/APP/src/components/alert-dlg/alert-dlg.model.ts b/APP/src/components/alert-dlg/alert-dlg.model.ts
new file mode 100644
index 0000000..60a283f
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.model.ts
@@ -0,0 +1,92 @@
+
+
+export enum ADIcon {
+ NONE = 'none',
+ INFO = 'info',
+ QUESTION = 'question',
+ WARNING = 'warning',
+ ERROR = 'error'
+}
+export enum ADButtons {
+ None,
+ OK,
+ OKCancel,
+ CancelOK,
+ YesNo,
+ YesNoCancel,
+ NoYes,
+}
+
+export enum ADButtonColor {
+ PRIMARY = 'primary',
+ SUCCESS = 'success',
+ INFO = 'info',
+ WARNING = 'warning',
+ DANGER = 'danger',
+}
+export declare type ADInputFiledType = null | 'string' | 'password' | 'number' | 'textarea';
+
+
+export class MessageBoxConfig {
+ title: string = '';
+ text: string = '';
+ inputTooltip: string = '';
+
+ /**
+ * Displayed Icon, Defaults to ADIcon.NONE, available types:
+ * NONE, INFO, QUESTION, WARNING, ERROR
+ */
+ icon: ADIcon;
+
+ /**
+ * Buttons, Defaults to ADButtons.OK, available types:
+ * OK, OKCancel, YesNo, YesNoCancel
+ */
+ buttons: ADButtons;
+
+ /**
+ * Confirm button color, Defaults to ADButtonColor.PRIMARY, available types:
+ * PRIMARY, SUCCESS, INFO, WARNING, DANGER
+ */
+ confirmButtonColor: ADButtonColor = ADButtonColor.PRIMARY;
+
+ /**
+ * Cancel button color, Defaults to ADButtonColor.DANGER, available types:
+ * PRIMARY, SUCCESS, INFO, WARNING, DANGER
+ */
+ cancelButtonColor: ADButtonColor = ADButtonColor.DANGER;
+
+ /**
+ * Close button color, Defaults to ADButtonColor.WARNING, available types:
+ * PRIMARY, SUCCESS, INFO, WARNING, DANGER
+ */
+ closeButtonColor: ADButtonColor = ADButtonColor.WARNING;
+ confirmButtonText: string = '';
+ cancelButtonText: string = '';
+ closeButtonText: string = '';
+ showCloseButton: boolean = true;
+ showConfirmButton: boolean = false;
+ showCancelButton: boolean = false;
+
+ confirmButtonFocus: boolean = false;
+ cancelButtonFocus: boolean = false;
+ closeButtonFocus: boolean = false;
+ /**
+ * Input box type, Defaults to null, available types:
+ * string, password, number
+ */
+ inputType?: ADInputFiledType = null;
+
+ /**
+ * Input box initial value
+ */
+ inputValue: string;
+
+ /**
+ * Input box input field mask
+ */
+ inputMask: string = '';
+ constructor(config: Partial) {
+ Object.assign(this, config);
+ }
+}
\ No newline at end of file
diff --git a/APP/src/components/alert-dlg/alert-dlg.module.ts b/APP/src/components/alert-dlg/alert-dlg.module.ts
new file mode 100644
index 0000000..2a1be06
--- /dev/null
+++ b/APP/src/components/alert-dlg/alert-dlg.module.ts
@@ -0,0 +1,28 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { AlertDlgComponent } from './alert-dlg.component';
+import { NbCardModule, NbButtonModule, NbIconModule, NbInputModule, NbTooltipModule, NbProgressBarModule } from '@nebular/theme';
+import { FormsModule } from '@angular/forms';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+import { InitFocusModule } from '../../directives/init-focus/init-focus.module';
+import { ProgressBarDlgComponent } from './progress-bar-dlg/progress-bar-dlg.component';
+@NgModule({
+ declarations: [AlertDlgComponent, ProgressBarDlgComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ NbCardModule,
+ NbButtonModule,
+ NbIconModule,
+ NbTooltipModule,
+ NbProgressBarModule,
+ MaskDirectiveModule,
+ InitFocusModule
+ ],
+ exports: [
+ AlertDlgComponent
+ ]
+}
+)
+export class AlertDlgModule { }
diff --git a/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.html b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.html
new file mode 100644
index 0000000..e6886b0
--- /dev/null
+++ b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.html
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ {{config.title}}
+
+
+ {{config.content}}
+
+
+ {{progressBarMessage}}
+
+ {{percent}}%
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.scss b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.scss
new file mode 100644
index 0000000..0c9c054
--- /dev/null
+++ b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.scss
@@ -0,0 +1,96 @@
+@import "../../../@theme/styles/themes";
+
+@include nb-install-component() {
+ .title {
+ //color: nb-theme(text-basic-color);
+ color: nb-theme(text-hint-color);
+ }
+ .content {
+ //color: nb-theme(text-hint-color);
+ color: nb-theme(text-basic-color);
+ }
+}
+
+.card {
+ min-width: 600px;
+ max-width: 90vw;
+ padding: 0 1.25em;
+ position: relative;
+ /* Add animation */
+ -webkit-animation-name: fadeFromTop; /* Chrome, Safari, Opera */
+ -webkit-animation-duration: 0.5s; /* Chrome, Safari, Opera */
+ animation-name: fadeFromTop;
+ animation-duration: 0.5s;
+}
+.title {
+ position: relative;
+ max-width: 100%;
+ margin: 0 0 0.6em;
+ padding: 0;
+ //color: #595959;
+ font-size: 1.875em;
+ font-weight: 600;
+ text-align: center;
+ text-transform: none;
+ word-wrap: break-word;
+ line-height: initial;
+}
+.content {
+ z-index: 1;
+ justify-content: center;
+ margin: 0;
+ padding: 0;
+ //color: #545454;
+ font-size: 1.125em;
+ font-weight: 400;
+ line-height: normal;
+ text-align: center;
+ word-wrap: break-word;
+}
+.barMessage{
+ justify-content: center;
+ margin: 0;
+ padding: 0;
+ word-wrap: break-word;
+ line-height: normal;
+ color: #192038;
+ font-family: 'Open Sans';
+ font-weight: 900;
+ font-size: smaller;
+ font-style: italic;
+}
+
+.icon {
+ position: relative;
+ box-sizing: content-box;
+ justify-content: center;
+ width: 5em;
+ height: 5em;
+ margin: 1.25em auto 1.875em;
+ border: 0.25em solid transparent;
+ border-radius: 50%;
+ font-family: inherit;
+ line-height: 5em;
+ cursor: default;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ &-content {
+ text-align: center;
+ align-items: center;
+ font-size: 3.75em;
+ nb-icon {
+ font-size: 4.6rem;
+ }
+ }
+}
+.question {
+ border-color: #c9dae1;
+ color: #87adbd;
+}
+
+.info {
+ border-color: #9de0f6;
+ color: #3fc3ee;
+}
diff --git a/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.spec.ts b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.spec.ts
new file mode 100644
index 0000000..4d85e14
--- /dev/null
+++ b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { ProgressBarDlgComponent } from './progress-bar-dlg.component';
+
+describe('ProgressBarDlgComponent', () => {
+ let component: ProgressBarDlgComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ProgressBarDlgComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ProgressBarDlgComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.ts b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.ts
new file mode 100644
index 0000000..9695238
--- /dev/null
+++ b/APP/src/components/alert-dlg/progress-bar-dlg/progress-bar-dlg.component.ts
@@ -0,0 +1,122 @@
+import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core';
+import { NbDialogRef } from '@nebular/theme';
+import { Subject } from 'rxjs';
+import { filter, takeUntil } from 'rxjs/operators';
+import { ProgressBarDlgConfig } from '../../../services/progress-bar-dlg.service';
+import { RbjDialogService } from '../../../services/rbj-dialog.service';
+import { SignalRService } from '../../../services/signal-r.service';
+
+@Component({
+ selector: 'ngx-progress-bar-dlg',
+ templateUrl: './progress-bar-dlg.component.html',
+ styleUrls: ['./progress-bar-dlg.component.scss']
+})
+export class ProgressBarDlgComponent implements OnInit {
+ private destroy$: Subject = new Subject();
+ private _percentIterateCount: number = 0;
+ percent: number = 25;
+
+ config: ProgressBarDlgConfig;
+
+ rainbowStatus = [
+ { status: 'danger', percent: 25 },
+ { status: 'warning', percent: 50 },
+ { status: 'info', percent: 75 },
+ { status: 'success', percent: 100 },
+ ]
+
+ public get progressBarStatus(): string {
+ return this.config.useRainbowStatus ? this.rainbowStatus.find(r => r.percent >= this.percent).status : this.config.status;
+ }
+ public get progressBarMessage(): string {
+ let message = this.percent >= 100 ? 'Finished' : this.config.message;
+ //if (this.config.showPercent) message += ` - ${this.percent}%`;
+ return message;
+ }
+
+ constructor(
+ private dlgService: RbjDialogService,
+ private cdRef: ChangeDetectorRef,
+ private dlgRef: NbDialogRef,
+ private signalRService: SignalRService
+ ) {
+
+ }
+
+ ngOnInit() {
+ if (this.config.enableIterators) {
+ setTimeout(() => {
+ this.percentageIteration();
+ }, 500);
+ }
+
+ if (this.config.signalRTag) {
+ this.signalRService.ApiMessageSubject.pipe(takeUntil(this.destroy$),
+ filter(r => r.tag == this.config.signalRTag))
+ .subscribe(result => {
+
+ let isProgressBarStatus = result.value['percentage'] > 0;
+
+ if (isProgressBarStatus) {
+ let progressStatus = result.value as ProgressBarStatus;
+ if (progressStatus) {
+ this.percent = Math.max(progressStatus.percentage, this.percent);
+ this.config.message = progressStatus.message;
+ this.cdRef.detectChanges();
+ if (this.percent >= 100) setTimeout(() => { this.dlgRef.close(true); }, 300);
+ }
+ } else {
+
+ let stage = this.config.signalRStages.find(s => s.signalRMsg == result.value);
+ if (stage) {
+ this.percent = Math.max(stage.percent, this.percent);
+ this.config.message = stage.message;
+ this.cdRef.detectChanges();
+ if (this.percent >= 100) setTimeout(() => { this.dlgRef.close(true); }, 300);
+ } else {
+ this.config.message = result.value;
+ }
+ }
+ });
+ }
+
+
+ this.dlgService.updateProgressBar$.pipe(takeUntil(this.destroy$))
+ .subscribe(result => {
+ this.percent = Math.max(result.percent, this.percent);
+ this.config.message = result.message;
+ this.cdRef.detectChanges();
+ if (this.percent >= 100) setTimeout(() => { this.dlgRef.close(true); }, 300);
+ });
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ percentageIteration() {
+ setTimeout(() => {
+ if (this.percent < 95) {
+ this.percent += 1;
+ this._percentIterateCount += 1;
+ if (this._percentIterateCount > 10) {
+ this._percentIterateCount = 0;
+ let stage = this.config.signalRStages.find(s => s.percent > this.percent);
+ if (stage && stage.message) this.config.message = stage.message;
+ }
+ this.percentageIteration();
+ }
+ }, this.percent < 60 ? 300 : this.percent < 80 ? 600 : 900);
+ }
+
+
+
+
+
+}
+
+export interface ProgressBarStatus {
+ message: string;
+ percentage: number;
+}
\ No newline at end of file
diff --git a/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.html b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.html
new file mode 100644
index 0000000..55f8bc9
--- /dev/null
+++ b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.html
@@ -0,0 +1,2 @@
+
\ No newline at end of file
diff --git a/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.scss b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.spec.ts b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.spec.ts
new file mode 100644
index 0000000..462987a
--- /dev/null
+++ b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { AutoSizingTextareaComponent } from './auto-sizing-textarea.component';
+
+describe('AutoSizingTextareaComponent', () => {
+ let component: AutoSizingTextareaComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AutoSizingTextareaComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AutoSizingTextareaComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.ts b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.ts
new file mode 100644
index 0000000..6065670
--- /dev/null
+++ b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.component.ts
@@ -0,0 +1,93 @@
+import { Component, ElementRef, ViewChild, OnChanges, Input, SimpleChanges, AfterViewInit, Output, EventEmitter, forwardRef } from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { TextareaUtils } from '../../utilities/textarea-utils';
+
+@Component({
+ selector: 'auto-sizing-textarea',
+ templateUrl: './auto-sizing-textarea.component.html',
+ styleUrls: ['./auto-sizing-textarea.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => AutoSizingTextareaComponent),
+ multi: true
+ }
+ ],
+})
+export class AutoSizingTextareaComponent implements ControlValueAccessor, AfterViewInit {
+
+ @Input('min-lines') public minLines: number = 1;
+ @Input('max-lines') public maxLines: number = 1;
+ @Input('default-lines') public defaultLines: number = 1;
+ @Output() public contentChange = new EventEmitter();
+ @ViewChild('textarea', { read: ElementRef }) textarea: ElementRef;
+
+ readonly: boolean
+ @Input("readonly")
+ public set input_readonly(value) {
+ this.readonly = typeof value !== "undefined" && value !== false;
+ }
+ onChange = (value: string) => { };
+ onTouched = () => { };
+ autoExpand = TextareaUtils.autoExpand;
+ private _content: string;
+ public get content(): string {
+ return this._content;
+ }
+ public set content(v: string) {
+
+ if (this._content != v) {
+ this._content = v;
+ this.onChange(v);
+ }
+
+ }
+ constructor() { }
+ writeValue(value: string): void {
+ this.content = value;
+ }
+
+ registerOnChange(fn: any): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ }
+
+ ngAfterViewInit() {
+ const computed = window.getComputedStyle(this.textarea.nativeElement);
+ const paddingTop = parseInt(computed.getPropertyValue('padding-top'));
+ const paddingBot = parseInt(computed.getPropertyValue('padding-bottom'));
+ const borderTop = parseInt(computed.getPropertyValue('border-top-width'));
+ const borderBot = parseInt(computed.getPropertyValue('border-bottom-width'));
+ const lineHeight = parseInt(computed.getPropertyValue('line-height'));
+ const extraHeight = borderTop + borderBot + paddingTop + paddingBot;
+ this.textarea.nativeElement.style.height = `${extraHeight + (this.defaultLines * lineHeight)}px`;
+ this.textarea.nativeElement.style.minHeight = `${extraHeight + (this.minLines * lineHeight)}px`;
+ this.textarea.nativeElement.style.maxHeight = `${extraHeight + (this.maxLines * lineHeight)}px`;
+
+
+
+ this.contentChanged();
+ }
+
+ ngOnChanges(changes: SimpleChanges) {
+ //
+ if (this.textarea && changes.content && changes.content.currentValue !== changes.content.previousValue) {
+ //
+ this.contentChanged();
+ }
+ }
+
+ contentChanged() {
+ // move to next frame to allow change to propagate to the control?
+ setTimeout(() => {
+ //
+ this.contentChange.emit(this.content);
+ TextareaUtils.autoExpand(this.textarea.nativeElement);
+ }, 0);
+ }
+
+}
diff --git a/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.module.ts b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.module.ts
new file mode 100644
index 0000000..fbb4543
--- /dev/null
+++ b/APP/src/components/auto-sizing-textarea/auto-sizing-textarea.module.ts
@@ -0,0 +1,24 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { AutoSizingTextareaComponent } from './auto-sizing-textarea.component';
+import { FormsModule } from '@angular/forms';
+import { NbInputModule } from '@nebular/theme';
+
+const routedComponents = [
+ AutoSizingTextareaComponent,
+];
+
+@NgModule({
+ declarations: [
+ ...routedComponents,
+ ],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ ],
+ exports: [
+ ...routedComponents,
+ ],
+})
+export class AutoSizingTextareaModule { }
diff --git a/APP/src/components/city-state-zip/city-state-zip.component.html b/APP/src/components/city-state-zip/city-state-zip.component.html
new file mode 100644
index 0000000..c1f8c2d
--- /dev/null
+++ b/APP/src/components/city-state-zip/city-state-zip.component.html
@@ -0,0 +1,31 @@
+
\ No newline at end of file
diff --git a/APP/src/components/city-state-zip/city-state-zip.component.scss b/APP/src/components/city-state-zip/city-state-zip.component.scss
new file mode 100644
index 0000000..0ba500f
--- /dev/null
+++ b/APP/src/components/city-state-zip/city-state-zip.component.scss
@@ -0,0 +1,10 @@
+@import "../../@theme/styles/themes";
+
+form.ng-touched input.ng-invalid {
+ border-color: nb-theme(color-danger-default);
+}
+
+input[type="text"][disabled] {
+ color: black;
+ background-color: white;
+}
diff --git a/APP/src/components/city-state-zip/city-state-zip.component.spec.ts b/APP/src/components/city-state-zip/city-state-zip.component.spec.ts
new file mode 100644
index 0000000..f429805
--- /dev/null
+++ b/APP/src/components/city-state-zip/city-state-zip.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { CityStateZipComponent } from './city-state-zip.component';
+
+describe('CityStateZipComponent', () => {
+ let component: CityStateZipComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ CityStateZipComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CityStateZipComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/city-state-zip/city-state-zip.component.ts b/APP/src/components/city-state-zip/city-state-zip.component.ts
new file mode 100644
index 0000000..842e451
--- /dev/null
+++ b/APP/src/components/city-state-zip/city-state-zip.component.ts
@@ -0,0 +1,376 @@
+import { Component, forwardRef, ViewChild, ElementRef, Input, Output, EventEmitter, Renderer2 } from '@angular/core';
+import { NG_VALUE_ACCESSOR, NG_VALIDATORS, ControlContainer, NgForm, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
+import { UuidUtils } from '../../utilities/uuid-utils';
+import { DropDownOption } from '../../entity/dropDownOption';
+import { ForceFocusMsgDirective } from '../../directives/force-focus-msg/force-focus-msg.directive';
+import { AddressInfo } from '../../models/contactInfo.model';
+import { MsgBoxService } from '../../services/msg-box.service';
+import { first, takeUntil } from 'rxjs/operators';
+import { ObjectUtils } from '../../utilities/object-utils';
+import { CityStateZipService, CityInfo } from '../../services/city-state-zip.service';
+import { Subject } from 'rxjs';
+import { StringUtils } from '../../utilities/string-utils';
+import { ADIcon } from '../alert-dlg/alert-dlg.model';
+
+var zipcodes = require('zipcodes-nrviens');
+
+@Component({
+ selector: 'city-state-zip',
+ templateUrl: './city-state-zip.component.html',
+ styleUrls: ['./city-state-zip.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CityStateZipComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => CityStateZipComponent),
+ multi: true,
+ },
+
+ ],
+ viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
+})
+export class CityStateZipComponent implements Validator {
+
+ @ViewChild('city', { static: true }) cityInput: ElementRef;
+ @ViewChild('state', { static: true }) stateInput: ElementRef;
+ @ViewChild('zip', { static: true }) zipInput: ElementRef;
+ @ViewChild(ForceFocusMsgDirective) statePopover: ForceFocusMsgDirective;
+
+ private _value: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
+ private _oldValue: AddressInfo = { city: '', state: '', zip: '', county: '' } as AddressInfo;
+ private _initialized: boolean;
+ private _legalInput: boolean = false;
+ private _readOnly: boolean;
+ private _disabled: boolean;
+
+ private _writing: boolean = true;
+ private _msgShown: boolean = false;
+ private destroy$: Subject = new Subject();
+ uuid = UuidUtils.generate();
+ disabledState: boolean = false;
+ required: boolean = false;
+ //zipCodeList: DropDownOption[] = [];
+
+ private _zipCodeList: DropDownOption[] = [new DropDownOption('', '')];
+ public get zipCodeList(): DropDownOption[] {
+ return this._zipCodeList;
+ }
+ public set zipCodeList(v: DropDownOption[]) {
+ if (v.length > 0) {
+ if (v.length != this._zipCodeList.length || v[0].value1 != this._zipCodeList[0].value1) {
+ this._zipCodeList = [new DropDownOption('', '')].concat(v);
+ }
+ } else {
+ this._zipCodeList = [new DropDownOption('', '')];
+ }
+
+ }
+
+
+ @Input() id?: string = ''
+ @Input() name?: string = ''
+ @Input() placeholder: string;
+ @Input() inputClass: string;
+
+ @Input() size: string = 'medium';
+ allData: any;
+
+ @Input()
+ public set readonly(value) {
+ this._readOnly = typeof value !== 'undefined' && value !== false;
+ }
+
+ @Input()
+ public set isDisabled(value) {
+ this._disabled = typeof value !== 'undefined' && value !== false;
+ }
+
+ public get isDisabled(): boolean {
+ return this._disabled;
+ }
+
+ @Input("required")
+ public set input_required(value) {
+ this.required = typeof value !== "undefined" && value !== false;
+ }
+
+
+ public get readonly(): boolean {
+ return this._readOnly;
+ }
+
+
+ public get value(): AddressInfo {
+ return this._value;
+ }
+ public set value(v: AddressInfo) {
+ if (this._value != v) {
+ this._value = v;
+ this.onChange(this.value);
+ }
+ }
+
+ public get city(): string {
+
+ return this.value.city;
+ }
+ @Input() public set city(v: string) {
+ if (this.value.city != v) {
+ this.value.city = v;
+ this.cityChange.emit(v);
+
+ this.onChange(this.value);
+ }
+ }
+
+ public get state(): string {
+ return this.value.state
+ }
+ @Input() public set state(v: string) {
+ if (this.value.state != v) {
+ this.value.state = v;
+ this.stateChange.emit(v);
+ //this.writeValue(this.value);
+ this.onChange(this.value);
+ }
+ }
+ public get zip(): string {
+
+ return this.value.zip;
+ }
+ @Input() public set zip(v: string) {
+
+ if (this.value.zip != v) {
+ this.value.zip = v;
+ this.zipChange.emit(v);
+ this.onChange(this.value);
+ }
+ }
+
+ public get county(): string {
+ return this.value.county;
+ }
+
+ public set county(v: string) {
+ if (this.value.county != v) {
+ this.value.county = v;
+ this.countyChange.emit(v);
+ this.onChange(this.value);
+ }
+ }
+
+ @Output() zipChange = new EventEmitter()
+
+ @Output() stateChange = new EventEmitter()
+ @Output() cityChange = new EventEmitter()
+ @Output() countyChange = new EventEmitter()
+
+ @Output() focus = new EventEmitter();
+ @Output() blur = new EventEmitter();
+
+ ready = new EventEmitter();
+
+ onChange = (value: AddressInfo) => { };
+ onTouched = () => { };
+
+ constructor(
+ private elementRef: ElementRef,
+ private msgBoxService: MsgBoxService,
+ private renderer: Renderer2,
+ private cszService: CityStateZipService,
+ ) {
+
+ }
+ ngOnInit() {
+ }
+ ngAfterViewInit() {
+ this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
+ this.ready.emit();
+ }
+
+ ngAfterContentInit() {
+ this._writing = false;
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+ onBlur() {
+ this.blur.emit(this.value);
+ }
+
+ validate(control: AbstractControl): ValidationErrors {
+ if (this.cszService.validateState(this.state)) {
+ if (this.statePopover) {
+ this.statePopover.invalid = false;
+ this.statePopover.hide();
+ }
+ }
+ else {
+ if (this.statePopover) {
+
+ this.statePopover.invalid = true;
+ this.statePopover.invalidMsg = `${this.state} isn't a valid state or U.S. territory mail code!`;
+ setTimeout(() => {
+ this.statePopover.show();
+ });
+ }
+ return { state: { message: 'Invalid state code.' } }
+ }
+
+ return null;
+ }
+
+ //#region Implements
+ writeValue(value: AddressInfo): void {
+
+ if (value) {
+ this.value = value;
+
+ //initial zip code with trimmed value
+ this.value.zip = StringUtils.getTrimmedValue(this.value.zip);
+
+ this._oldValue = ObjectUtils.Clone(value);
+
+ const city = this.cszService.lookUpZipCode(this.city, this.state);
+ if (city != null) {
+ this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
+ }
+ }
+
+ this.onChange(this.value);
+ }
+
+ registerOnChange(fn: (value: AddressInfo) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabledState = isDisabled;
+ }
+
+ zipCodeChanged(inputZip: string) {
+
+ if (!this._writing && !this.readonly &&
+ StringUtils.getTrimmedValue(this._oldValue.zip) != StringUtils.getTrimmedValue(inputZip)
+ ) {
+
+ this._oldValue.city = null;
+ this._writing = true;
+ if (inputZip && inputZip.length < 10 && inputZip.length > 5) {
+
+ inputZip = inputZip.substring(0, 5);
+ }
+ this.value.zip = inputZip;
+
+
+ const city = this.cszService.lookUpCity(this.zip);
+
+ if (city != null) {
+ this.city = city.city;
+ this.state = city.state;
+
+ this.zipCodeList = this.cszService
+ .lookUpZipCode(this.city, this.state)
+ .zipCode
+ .map((zipCode, i) => new DropDownOption(zipCode, zipCode));
+ this.zipcodeToCounty(this.zip);
+ }
+
+ setTimeout(() => {
+ this.onTouched();
+ this._writing = false;
+ }, 200);
+ }
+ }
+
+ cityOrStateChanged() {
+ if (this.value.city) {
+ this.value.city = this.value.city.replace(/^\s+|\s+$/g, '');
+ }
+
+ if (!this._writing && !this.readonly) {
+ if (this._oldValue.city != this.city || this._oldValue.state != this.state) {
+ this._writing = true;
+
+ if (this.city && this.state && this.state.length == 2) {
+ const city = this.updateZipCodesFromCity();
+ if (city != null) {
+ this.city = city.city;
+ this.state = city.state;
+ }
+ else {
+ if (false == this._msgShown) {
+ this._msgShown = true;
+ this.msgBoxService.show("Zip Code Not Found",
+ {
+ text: `Zip code for ${this.city}, ${this.state} not found.`,
+ icon: ADIcon.WARNING
+ })
+ .pipe(first()).subscribe(result => {
+ this._msgShown = false;
+ });
+ }
+ }
+ }
+
+ this._oldValue = ObjectUtils.Clone(this.value);
+ setTimeout(() => {
+
+ this.onTouched();
+ this._writing = false;
+ }, 200);
+ }
+ }
+ }
+
+ clearZipCode() {
+ this.zipCodeList = [];
+ this.zip = '';
+ }
+
+ zipcodeToCounty(zip) {
+
+ if (zip) {
+ this.cszService.getCounty(zip).subscribe((data) => {
+ this.allData = data;
+
+ if (this.allData) {
+
+ let countyName = this.allData.County;
+ countyName = countyName.toLowerCase().split(" ");
+ for (let i = 0; i < countyName.length; i++) {
+ countyName[i] = countyName[i][0].toUpperCase() + countyName[i].substr(1);
+ }
+ this.county = countyName.join(" ");
+
+
+ }
+ });
+
+ // const countySearch = zipcodes.lookup(zip);
+ // if (countySearch) this.county = countySearch.county;
+
+ }
+ }
+
+ private updateZipCodesFromCity(): CityInfo {
+ this.state = this.state.toUpperCase();
+ const city = this.cszService.lookUpZipCode(this.city, this.state);
+ if (city != null) {
+ this.zipCodeList = city.zipCode.map((zipCode, i) => new DropDownOption(zipCode, zipCode));
+ } else {
+ this.zipCodeList = [];
+ }
+ return city;
+ }
+}
diff --git a/APP/src/components/city-state-zip/city-state-zip.module.ts b/APP/src/components/city-state-zip/city-state-zip.module.ts
new file mode 100644
index 0000000..2d2e802
--- /dev/null
+++ b/APP/src/components/city-state-zip/city-state-zip.module.ts
@@ -0,0 +1,24 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CityStateZipComponent } from './city-state-zip.component';
+import { NbInputModule } from '@nebular/theme';
+import { FormsModule } from '@angular/forms';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+import { DropDownListModule } from '../drop-down-list/drop-down-list.module';
+import { ForceFocusMsgModule } from '../../directives/force-focus-msg/force-focus-msg.module';
+import { RbjTooltipModule } from '../../directives/rbj-tooltip/rbj-tooltip.module';
+
+@NgModule({
+ declarations: [CityStateZipComponent],
+ imports: [
+ CommonModule,
+ NbInputModule,
+ FormsModule,
+ MaskDirectiveModule,
+ DropDownListModule,
+ ForceFocusMsgModule,
+ RbjTooltipModule,
+ ],
+ exports: [CityStateZipComponent],
+})
+export class CityStateZipModule { }
diff --git a/APP/src/components/components.component.ts b/APP/src/components/components.component.ts
new file mode 100644
index 0000000..f07e322
--- /dev/null
+++ b/APP/src/components/components.component.ts
@@ -0,0 +1,14 @@
+// import { Component, OnInit } from '@angular/core';
+
+// @Component({
+// selector: 'ngx-components',
+// template: ''
+// })
+// export class ComponentsComponent implements OnInit {
+
+// constructor() { }
+
+// ngOnInit() {
+// }
+
+// }
diff --git a/APP/src/components/components.module.ts b/APP/src/components/components.module.ts
new file mode 100644
index 0000000..c2c7fec
--- /dev/null
+++ b/APP/src/components/components.module.ts
@@ -0,0 +1,13 @@
+import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+ imports: [
+ FormsModule,
+ CommonModule,
+ ],
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
+ declarations: [],
+})
+export class ComponentsModule { }
diff --git a/APP/src/components/context-menu/context-menu.component.html b/APP/src/components/context-menu/context-menu.component.html
new file mode 100644
index 0000000..381afff
--- /dev/null
+++ b/APP/src/components/context-menu/context-menu.component.html
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+ {{item.title}}
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/context-menu/context-menu.component.scss b/APP/src/components/context-menu/context-menu.component.scss
new file mode 100644
index 0000000..b1a354d
--- /dev/null
+++ b/APP/src/components/context-menu/context-menu.component.scss
@@ -0,0 +1,44 @@
+@import "../../@theme/styles/themes";
+
+
+.transparent {
+ opacity: 0;
+}
+
+li.list-group-item {
+ cursor: pointer;
+ border: 0;
+}
+@include nb-install-component() {
+
+ .list-group-item {
+ background-color: nb-theme(background-basic-color-1);
+
+}
+}
+.disabled {
+ color: #a2acc0;
+ cursor: default !important;
+}
+
+@include nb-install-component() {
+
+ // .groupStart {
+ // border-top: 1px solid nb-theme(border-basic-color-5);
+ // }
+
+ .groupSeparator {
+ // height: 1px;
+ // overflow-y: auto;
+ display: inline-block;
+
+ > hr {
+ // padding-top: 1px;
+ // padding-bottom: 1px;
+ margin-top: 1px;
+ margin-bottom: 1px;
+ }
+ }
+
+}
+
diff --git a/APP/src/components/context-menu/context-menu.component.spec.ts b/APP/src/components/context-menu/context-menu.component.spec.ts
new file mode 100644
index 0000000..17f7c93
--- /dev/null
+++ b/APP/src/components/context-menu/context-menu.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { ContextMenuComponent } from './context-menu.component';
+
+describe('ContextMenuComponent', () => {
+ let component: ContextMenuComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ContextMenuComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ContextMenuComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/context-menu/context-menu.component.ts b/APP/src/components/context-menu/context-menu.component.ts
new file mode 100644
index 0000000..9c96d9c
--- /dev/null
+++ b/APP/src/components/context-menu/context-menu.component.ts
@@ -0,0 +1,74 @@
+import { Component, ViewChild, Input, AfterViewInit, ElementRef } from '@angular/core';
+import { NbDialogRef, NbDialogService } from '@nebular/theme';
+import { ContextMenuItem } from '../../directives/right-click-menu/context-menu-item.model';
+import { Direction } from '../../directives/right-click-menu/direction.enum';
+import { RbjDialogService } from '../../services/rbj-dialog.service';
+
+@Component({
+ selector: 'ngx-context-menu',
+ templateUrl: './context-menu.component.html',
+ styleUrls: ['./context-menu.component.scss']
+})
+export class ContextMenuComponent implements AfterViewInit {
+
+ @ViewChild('menu') menu: ElementRef;
+
+ selectedItem: string;
+
+ @Input() public Value: any;
+ @Input() public X: number;
+ @Input() public Y: number;
+
+ ContextMenuItems: ContextMenuItem[];
+
+ @Input() public Direction: Direction;
+
+ constructor(
+ protected ref: NbDialogRef,
+ private dialogService: RbjDialogService,
+ ) { }
+
+ ngAfterViewInit(): void {
+ this.menu.nativeElement.style.position = "absolute";
+ this.menu.nativeElement.style.left = this.X + 'px';
+ this.menu.nativeElement.style.top = this.Y + 'px';
+
+ // switch (this.Direction) {
+ // case Direction.Auto:
+ // case Direction.Down:
+ // this.menu.nativeElement.style.left = this.X + 'px';
+ // this.menu.nativeElement.style.top = this.Y + 'px';
+ // case Direction.Up:
+ // this.menu.nativeElement.style.left = this.X + 'px';
+ // this.menu.nativeElement.style.bottom = this.Y + 'px';
+ // case Direction.Left:
+ // this.menu.nativeElement.style.left = this.X + 'px';
+ // this.menu.nativeElement.style.top = this.Y + 'px';
+ // case Direction.Right:
+ // this.menu.nativeElement.style.left = this.X + 'px';
+ // this.menu.nativeElement.style.top = this.Y + 'px';
+ // }
+ }
+
+ runItemCallback(item: ContextMenuItem, event: any) {
+
+ if (item.contextMenuItems) {
+ this.dialogService.open(ContextMenuComponent, {
+ backdropClass: "transparent",
+ context: {
+ Value: this,
+ X: this.X + this.menu.nativeElement.width,
+ Y: event.y - event.offsetY,
+ ContextMenuItems: item.contextMenuItems,
+ Direction: Direction.Right,
+ },
+ closeOnBackdropClick: true,
+ });
+ }
+ else {
+ this.ref.close();
+ setTimeout(() => item && item.callback && item.callback(this.Value, event.target as HTMLElement));
+ }
+ }
+
+}
diff --git a/APP/src/components/context-menu/context-menu.module.ts b/APP/src/components/context-menu/context-menu.module.ts
new file mode 100644
index 0000000..c717f4e
--- /dev/null
+++ b/APP/src/components/context-menu/context-menu.module.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { ContextMenuComponent } from './context-menu.component';
+import { NbButtonModule, NbIconModule, NbTooltipModule, NbMenuModule, NbCardModule } from '@nebular/theme';
+
+const components = [
+ ContextMenuComponent,
+]
+
+@NgModule({
+ declarations: [...components],
+ imports: [
+ CommonModule,
+
+ NbButtonModule,
+ NbCardModule,
+ NbIconModule,
+ NbMenuModule,
+ NbTooltipModule,
+ ],
+ exports: [...components],
+})
+export class ContextMenuModule { }
diff --git a/APP/src/components/currency-input/currency-input.component.html b/APP/src/components/currency-input/currency-input.component.html
new file mode 100644
index 0000000..7d67bd1
--- /dev/null
+++ b/APP/src/components/currency-input/currency-input.component.html
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/APP/src/components/currency-input/currency-input.component.scss b/APP/src/components/currency-input/currency-input.component.scss
new file mode 100644
index 0000000..bcdecde
--- /dev/null
+++ b/APP/src/components/currency-input/currency-input.component.scss
@@ -0,0 +1,15 @@
+@import "../../@theme/styles/themes";
+
+form.ng-touched input.ng-invalid {
+ border-color: nb-theme(color-danger-default);
+}
+
+:host {
+ flex: auto;
+ display: contents;
+}
+input:read-only {
+ background-color: transparent;
+ box-shadow: none;
+ border-color: nb-theme(border-primary-color-1);
+}
diff --git a/APP/src/components/currency-input/currency-input.component.spec.ts b/APP/src/components/currency-input/currency-input.component.spec.ts
new file mode 100644
index 0000000..9f3b2b6
--- /dev/null
+++ b/APP/src/components/currency-input/currency-input.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { CurrencyInputComponent } from './currency-input.component';
+
+describe('CurrencyInputComponent', () => {
+ let component: CurrencyInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ CurrencyInputComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CurrencyInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/currency-input/currency-input.component.ts b/APP/src/components/currency-input/currency-input.component.ts
new file mode 100644
index 0000000..48e83fd
--- /dev/null
+++ b/APP/src/components/currency-input/currency-input.component.ts
@@ -0,0 +1,331 @@
+import { Component, Input, Output, EventEmitter, forwardRef, ViewChild, ElementRef, Renderer2 } from '@angular/core';
+import { formatCurrency } from '@angular/common';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR, Validator, AbstractControl, ValidationErrors, NG_VALIDATORS } from '@angular/forms';
+import { NbTooltipDirective, NbTrigger } from '@nebular/theme';
+import { Subject } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
+import { StringUtils } from '../../utilities/string-utils';
+
+@Component({
+ selector: 'rbj-currency-input',
+ templateUrl: './currency-input.component.html',
+ styleUrls: ['./currency-input.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => CurrencyInputComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => CurrencyInputComponent),
+ multi: true,
+ },
+ ]
+})
+export class CurrencyInputComponent implements ControlValueAccessor, Validator {
+
+ @ViewChild('currencyInput', { static: true }) input: ElementRef;
+ @ViewChild(NbTooltipDirective) tooltip: NbTooltipDirective;
+ private destroy$: Subject = new Subject();
+ private _ngModel: number;
+ private _reverseStatus: boolean = false;
+ private _disableStatus: boolean = false;
+ private _initialized: boolean;
+ private _lastBlurValue: number;
+ realTimeUpdate: boolean
+ status = '';
+ displayValue: string;
+ readOnly: boolean = false;
+ required: boolean = false;
+ leftToRight: boolean = false;
+ @Input() id?= "";
+ @Input() name = "";
+ onChange = (value: number) => { };
+ onTouched = () => { };
+ @Input() min = -999999999.99;
+ @Input() max = 999999999.99;
+ @Input() currencyStep = '0.01';
+ @Input() class = '';
+
+
+ /**
+ * A calculated best value should be for this field, if got mismatch will have hover message
+ */
+ @Input() valueShouldBe: number = null;
+ /**
+ * The hover message format for mismatch, Default:`The value should be {1}`, `{1}` will be replace with formatted value
+ */
+ @Input() valueShouldBeWarningText: string = "The value should be {1}";
+ /**
+ * The hover message color default:warning | empty, primary, info, success, warning, danger
+ */
+ @Input() valueShouldBeWarningStatus = 'warning';
+
+ @Input() zeroStatus = '';
+ @Input() zeroExpression = '';
+ @Input() nullExpression = '';
+
+ /**
+ * Positive value status color default:empty | empty, primary, info, success, warning, danger
+ */
+ @Input() positiveStatus = '';
+
+ /**
+ * Negative value status color default:danger | primary, info, success, warning, danger
+ */
+ @Input() negativeStatus = 'danger';
+ @Input() currencyCode = 'USD';
+
+ @Input() nullValue = 0;
+ @Input() disabled = false;
+ @Input() precision = 2;
+ @Input() fieldSize = 'medium';
+ @Input()
+ public set reverseStatus(value) {
+ this._reverseStatus = typeof value !== 'undefined' && value !== false;
+ this.setStatusColor();
+ }
+ @Input()
+ public set disableStatus(value) {
+ this._disableStatus = typeof value !== 'undefined' && value !== false;
+ this.setStatusColor();
+ }
+
+ @Input("readonly")
+ public set input_readOnly(value) {
+ this.readOnly = typeof value !== "undefined" && value !== false;
+ }
+
+ @Input("required")
+ public set input_required(value) {
+ this.required = typeof value !== "undefined" && value !== false;
+ }
+
+ @Input("LTR")
+ public set input_leftToRight(value) {
+ this.leftToRight = typeof value !== "undefined" && value !== false;
+ }
+ @Input("realTimeUpdate")
+ public set input_realTimeUpdate(value) {
+ this.realTimeUpdate = typeof value !== "undefined" && value !== false;
+ }
+ @Input("tooltips") tooltipContent: string;
+ @Output() change = new EventEmitter();
+ @Output() focus = new EventEmitter();
+ @Output() blur = new EventEmitter();
+
+ constructor(
+ private elementRef: ElementRef,
+ private renderer: Renderer2) {
+
+ }
+ validate(control: AbstractControl): ValidationErrors {
+ if (this.required && (this.value == null || this.value == 0)) {
+ return { "currency": "" };
+ }
+
+ return null;
+ }
+ registerOnValidatorChange?(fn: () => void): void {
+ }
+ //#region Implements
+ writeValue(value: string): void {
+ if (value) {
+ this.value = Number(value);
+ } else {
+ this.value = this.nullValue;
+ }
+ this._lastBlurValue = this.value;
+ //this.onChange(this.value);
+ this.setDisplayValue(this.value);
+ this.setStatusColor();
+ }
+ registerOnChange(fn: (value: number) => void): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ ngAfterViewInit() {
+ this.tooltip.nbTooltipShowStateChange
+ .pipe(takeUntil(this.destroy$))
+ .subscribe(isShown => {
+ if (!this.tooltipContent && isShown) {
+ this.tooltip.hide();
+ }
+ })
+ this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
+ }
+
+ ngOnInit() {
+ // if (this.readOnly) {
+ // this.positiveStatus = '';
+ // this.negativeStatus = '';
+ // }
+ }
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ //#endregion
+
+
+ public get value(): number {
+ return this._ngModel;
+ }
+ public set value(v: number) {
+ if (v) v = Number(v.toFixed(this.precision));
+ if (v < this.min) {
+ v = this.min;
+ } else if (v > this.max) {
+ v = this.max;
+ }
+
+ if (this._initialized) {
+ if (v != this._ngModel) {
+
+ this._ngModel = v;
+ this.onChange(this.value);
+
+
+ }
+ } else {
+ this._initialized = true;
+ this._ngModel = v;
+ }
+ }
+ onBlur() {
+ this.setDisplayValue(this.value);
+ if (this.value != this._lastBlurValue) {
+
+ this.change.emit(this.value);
+ this.onChange(this.value);
+ this._lastBlurValue = this.value;
+ this.onTouched();
+ this.blur.emit(this.value);
+ this.setStatusColor();
+ }
+ }
+ setDisplayValue(v: number) {
+ if (null === v) {
+ this.displayValue = this.nullExpression;
+ } else if (['', 0, undefined, NaN].includes(v)) {
+
+ this.displayValue = this.zeroExpression;
+ } else {
+ this.displayValue = formatCurrency(v, "en", "", this.currencyCode, `0.${this.precision}`);
+ }
+ //to avoid input 0 twice will not update empty to ui
+ this.input.nativeElement.value = this.displayValue;
+ }
+
+ currencyInputChanged(value: string) {
+ if (this.displayValue != value) {
+
+ if (this.realTimeUpdate) {
+ this.value = Number(value.replace('..', '.').replace(/[$,]/g, ''));
+
+ if (false == [0, null, undefined, NaN].includes(this.value)) {
+ let focus = this.input.nativeElement.selectionStart;
+ let lengthBeforeCursor = this.input.nativeElement.value.substring(0, focus).replace(',', '').length;
+ this.setDisplayValue(this.value);
+
+ let formattedValue = this.input.nativeElement.value;
+ let commaCount = 0;
+ for (let i = 0; i < lengthBeforeCursor; i++) {
+ const c = formattedValue[i];
+ if (c == ',') commaCount++;
+ }
+
+ this.setCursorPosition(lengthBeforeCursor + commaCount);
+ } else {
+
+ //this.setDisplayValue(this.value);
+ }
+ } else {
+ this.value = StringUtils.isNullOrWhitespace(value) ? this.nullValue : Number(value.replace(/[$,]/g, ''));
+ }
+
+
+
+
+ }
+ }
+
+
+ setCursorPosition(position) {
+ let input = this.input.nativeElement;
+ if (input.setSelectionRange) {
+ input.focus();
+ input.setSelectionRange(position, position);
+ } else if (input.createTextRange) {
+ var range = input.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', position);
+ range.moveStart('character', position);
+ range.select();
+ }
+ }
+ setStatusColor() {
+ if (this._disableStatus) {
+ this.status = "";
+ return;
+ }
+
+ if (this._ngModel > 0) this.status = this._reverseStatus ? this.negativeStatus : this.positiveStatus;
+ else if (['', 0, null, undefined, NaN].includes(this._ngModel)) {
+ this.status = this.zeroStatus;
+ }
+ else {
+ this.status = this._reverseStatus ? this.positiveStatus : this.negativeStatus;
+ }
+
+ if (this.valueShouldBe != null) {
+ let shouldBe = formatCurrency(this.valueShouldBe, "en", "", this.currencyCode);
+ if (this.displayValue != (this.valueShouldBe == 0 ? this.zeroExpression : shouldBe)) {
+
+ this.status = this.valueShouldBeWarningStatus;
+ this.tooltipContent = this.valueShouldBeWarningText.replace('{1}', shouldBe);
+ } else {
+
+ this.tooltipContent = '';
+ }
+ }
+ }
+ get alignmentClass(): string {
+ return this.leftToRight ? "text-left" : "text-right";
+ }
+
+ public onKeyDown(e: KeyboardEvent): void {
+
+
+ if ([46, 8, 9, 27, 13, 110, 190].indexOf(e.keyCode) !== -1 ||
+ // Allow: Ctrl+A
+ (e.keyCode === 65 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+C
+ (e.keyCode === 67 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+V
+ (e.keyCode === 86 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: Ctrl+X
+ (e.keyCode === 88 && (e.ctrlKey || e.metaKey)) ||
+ // Allow: page up, page down, home, end, left, right
+ (e.keyCode >= 33 && e.keyCode <= 39)) {
+ // let it happen, don't do anything
+ return;
+ }
+
+ let regMatch = e.key.match(/[\d.,-]/g);
+ if (regMatch == null) {
+ //e.stopPropagation()
+ e.preventDefault();
+ }
+ }
+
+
+}
diff --git a/APP/src/components/currency-input/currency-input.module.ts b/APP/src/components/currency-input/currency-input.module.ts
new file mode 100644
index 0000000..9b3e7e0
--- /dev/null
+++ b/APP/src/components/currency-input/currency-input.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { CurrencyInputComponent } from './currency-input.component';
+import { FormsModule } from '@angular/forms';
+import { NbInputModule, NbTooltipModule } from '@nebular/theme';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+
+@NgModule({
+ declarations: [CurrencyInputComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ NbTooltipModule,
+ MaskDirectiveModule
+ ],
+ exports: [CurrencyInputComponent]
+})
+export class CurrencyInputModule { }
diff --git a/APP/src/components/date-input/calendar-view/calendar-view.component.html b/APP/src/components/date-input/calendar-view/calendar-view.component.html
new file mode 100644
index 0000000..187e5eb
--- /dev/null
+++ b/APP/src/components/date-input/calendar-view/calendar-view.component.html
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/date-input/calendar-view/calendar-view.component.scss b/APP/src/components/date-input/calendar-view/calendar-view.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/date-input/calendar-view/calendar-view.component.spec.ts b/APP/src/components/date-input/calendar-view/calendar-view.component.spec.ts
new file mode 100644
index 0000000..445ff81
--- /dev/null
+++ b/APP/src/components/date-input/calendar-view/calendar-view.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { CalendarViewComponent } from './calendar-view.component';
+
+describe('CalendarViewComponent', () => {
+ let component: CalendarViewComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ CalendarViewComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CalendarViewComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/date-input/calendar-view/calendar-view.component.ts b/APP/src/components/date-input/calendar-view/calendar-view.component.ts
new file mode 100644
index 0000000..ea94b0c
--- /dev/null
+++ b/APP/src/components/date-input/calendar-view/calendar-view.component.ts
@@ -0,0 +1,183 @@
+import { Component, HostBinding, HostListener, Input, OnInit, Renderer2, Type } from '@angular/core';
+import { NbCalendarCell, NbCalendarRange, NbCalendarRangeYearCellComponent, NbCalendarSize, NbCalendarViewMode, NbCalendarViewModeValues, NbCalendarYearModelService, NbDateService, NbPositionedContainerComponent, NbRenderableContainer } from '@nebular/theme';
+import { Subject } from 'rxjs';
+
+@Component({
+ selector: 'ngx-calendar-view',
+ templateUrl: './calendar-view.component.html',
+ styleUrls: ['./calendar-view.component.scss']
+})
+export class CalendarViewComponent extends NbPositionedContainerComponent implements NbRenderableContainer {
+
+ /**
+ * Defines active view for calendar.
+ * */
+ @Input('startView') mode: NbCalendarViewMode = NbCalendarViewMode.DATE;
+ static ngAcceptInputType_activeViewMode: NbCalendarViewModeValues;
+
+ /**
+ * Determines whether we should show calendar navigation or not.
+ * */
+ @Input()
+ @HostBinding('class.has-navigation')
+ showNavigation: boolean = true;
+
+ date: Date;
+ visibleDate: Date;
+ ViewMode = NbCalendarViewMode;
+ max: Date;
+ min: Date;
+ size: NbCalendarSize = NbCalendarSize.MEDIUM;
+ context: contextSetting;
+ private listener: any;
+ private destroy$: Subject = new Subject();
+ constructor(
+ protected dateService: NbDateService,
+ private renderer: Renderer2,
+ protected yearModelService: NbCalendarYearModelService,
+ ) {
+ super();
+ }
+ renderContent() {
+
+ }
+ filter: (Date) => boolean;
+ private hasParentClass(child, classname) {
+ if (child.className && child.className.split(' ').indexOf(classname) >= 0) return true;
+ try {
+ //Throws TypeError if child doesn't have parent any more
+ return child.parentNode && this.hasParentClass(child.parentNode, classname);
+ } catch (TypeError) {
+ return false;
+ }
+ }
+ ngOnInit() {
+ this.date = this.context.date;
+ this.visibleDate = this.date ? this.date : this.dateService.today();
+
+
+ setTimeout(() => {
+ this.listener = this.renderer.listen('window', 'click', (e: Event) => {
+ //
+ let el = (e.target as HTMLElement);
+ if (
+ this.hasParentClass(el, 'rbjCalendar')
+ // el.className.indexOf('rbjCalendar') > -1 ||
+ // el.tagName.indexOf('NB-CALENDAR') > -1
+ ) {
+ this.context.focus();
+ } else {
+ this.context.hide();
+ }
+ })
+ }, 50);
+
+ }
+
+ ngOnDestroy() {
+ if (this.listener) { this.listener(); }
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+ setViewMode(mode: NbCalendarViewMode) {
+ this.mode = mode;
+ }
+ setVisibleDate(date: Date) {
+
+ }
+
+ prevMonth() {
+ this.changeVisibleMonth(-1);
+ }
+
+ nextMonth() {
+ this.changeVisibleMonth(1);
+ }
+
+ prevYear() {
+ this.changeVisibleYear(-1);
+ }
+
+ nextYear() {
+ this.changeVisibleYear(1);
+ }
+
+ prevYears() {
+ this.changeVisibleYears(-1);
+ }
+
+ nextYears() {
+ this.changeVisibleYears(1);
+ }
+
+
+ navigateNext() {
+ switch (this.mode) {
+ case NbCalendarViewMode.DATE:
+ return this.nextMonth();
+ case NbCalendarViewMode.MONTH:
+ return this.nextYear();
+ case NbCalendarViewMode.YEAR:
+ return this.nextYears();
+ }
+ }
+ onChangeViewMode() {
+ if (this.mode === NbCalendarViewMode.DATE) {
+ return this.setViewMode(NbCalendarViewMode.YEAR);
+ }
+
+ this.setViewMode(NbCalendarViewMode.DATE);
+ }
+
+ private changeVisibleMonth(direction: number) {
+ this.visibleDate = this.dateService.addMonth(this.visibleDate, direction);
+ }
+
+ private changeVisibleYear(direction: number) {
+ this.visibleDate = this.dateService.addYear(this.visibleDate, direction);
+ }
+
+ private changeVisibleYears(direction: number) {
+ this.visibleDate = this.dateService.addYear(this.visibleDate, direction * this.yearModelService.getYearsInView());
+ }
+
+ navigateToday() {
+
+ this.visibleDate = this.dateService.today();
+
+ this.context.dateChange(this.dateService.today());
+ }
+
+
+ onChange(date: Date) {
+ this.context.dateChange(date);
+ }
+
+ @HostListener('focus')
+ focus() {
+ this.context.focus();
+ }
+
+ dayCellComponent: Type>>
+
+ /**
+ * Custom month cell component. Have to implement `NbCalendarCell` interface.
+ * */
+ monthCellComponent: Type>>;
+
+ /**
+ * Custom year cell component. Have to implement `NbCalendarCell` interface.
+ * */
+ yearCellComponent: Type>> = NbCalendarRangeYearCellComponent;
+
+
+
+}
+
+
+export interface contextSetting {
+ date: Date,
+ hide: () => {},
+ focus: () => {},
+ dateChange: (d: Date) => {};
+}
\ No newline at end of file
diff --git a/APP/src/components/date-input/date-input.component.html b/APP/src/components/date-input/date-input.component.html
new file mode 100644
index 0000000..92644f9
--- /dev/null
+++ b/APP/src/components/date-input/date-input.component.html
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/APP/src/components/date-input/date-input.component.scss b/APP/src/components/date-input/date-input.component.scss
new file mode 100644
index 0000000..12cdffe
--- /dev/null
+++ b/APP/src/components/date-input/date-input.component.scss
@@ -0,0 +1,13 @@
+@import "../../@theme/styles/themes";
+
+form.ng-touched input.ng-invalid {
+ border-color: nb-theme(color-danger-default);
+}
+:host {
+ display: block;
+}
+
+.editBtn {
+ width: 29px !important;
+ padding: 0 !important;
+}
diff --git a/APP/src/components/date-input/date-input.component.spec.ts b/APP/src/components/date-input/date-input.component.spec.ts
new file mode 100644
index 0000000..edde824
--- /dev/null
+++ b/APP/src/components/date-input/date-input.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { DateInputComponent } from './date-input.component';
+
+describe('DateInputComponent', () => {
+ let component: DateInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DateInputComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DateInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/date-input/date-input.component.ts b/APP/src/components/date-input/date-input.component.ts
new file mode 100644
index 0000000..d7484ce
--- /dev/null
+++ b/APP/src/components/date-input/date-input.component.ts
@@ -0,0 +1,375 @@
+import { Component, OnInit, ChangeDetectionStrategy, forwardRef, ViewChild, ElementRef, Output, Input, EventEmitter, Renderer2, ChangeDetectorRef } from '@angular/core';
+import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validator, AbstractControl, ValidationErrors } from '@angular/forms';
+import { DatePipe } from '@angular/common';
+import { ApplyMaskService } from '../../services/apply-mask.service';
+import { ForceFocusMsgDirective } from '../../directives/force-focus-msg/force-focus-msg.directive';
+import { NbAdjustment, NbDatepickerComponent, NbDynamicOverlay, NbDynamicOverlayHandler, NbPosition, NbTrigger } from '@nebular/theme';
+import { MsgBoxService } from '../../services/msg-box.service';
+import { first } from 'rxjs/operators';
+import { StringUtils } from '../../utilities/string-utils';
+import { CalendarViewComponent } from './calendar-view/calendar-view.component';
+import { Subject } from 'rxjs';
+import { DateUtilsService } from '../../services/date-utils.service';
+import { ADIcon } from '../alert-dlg/alert-dlg.model';
+const DATE_FORMAT = 'MM/dd/yyyy'
+@Component({
+ selector: 'rbj-date-input',
+ templateUrl: './date-input.component.html',
+ styleUrls: ['./date-input.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => DateInputComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => DateInputComponent),
+ multi: true,
+ },
+ NbDynamicOverlayHandler,
+ NbDynamicOverlay
+ ]
+})
+export class DateInputComponent implements OnInit, Validator {
+
+ @ViewChild('datePickerContainer', { static: true }) datePickerContainer: ElementRef;
+ @ViewChild('inputBox', { static: true }) input: ElementRef;
+ @ViewChild(ForceFocusMsgDirective) popover: ForceFocusMsgDirective;
+ private _value: Date;
+ private _lastBlurValue: Date;
+
+ private dynamicOverlay: NbDynamicOverlay;
+ private _calendarIsShown: boolean = false;
+ private _passwordTag: string = null;
+
+ _readOnly: boolean;
+
+ //this is for password protected field
+ passwordSecured: boolean = false;
+
+ onChange = (value: Date) => { };
+ onTouched = () => { };
+ name: string;
+ invalid: boolean = false;
+ @Input() id?: string = "";
+ @Input() placeholder: string;
+ @Input() inputClass: string;
+ @Input() size: string = 'medium';
+ @Input() min: Date;
+ @Input() max: Date;
+ @Input() maskExpression: string = '00/00/0000';
+ _disabled = false;
+ _required = false;
+
+ @Input()
+ public set readonly(value) {
+ this._readOnly = typeof value !== 'undefined' && value !== false;
+ }
+ @Input()
+ public set required(value) {
+ this._required = typeof value !== 'undefined' && value !== false;
+ }
+
+ skipHoliday: boolean
+ @Input("skipHoliday")
+ public set input_skipHoliday(value) {
+ this.skipHoliday = typeof value !== "undefined" && value !== false;
+ }
+
+ @Input()
+ public set passwordTag(value) {
+ this._passwordTag = value;
+ this.passwordSecured = true;
+ }
+ public get passwordTag() {
+ return this._passwordTag;
+ }
+
+ public get showUnlockBtn(): boolean {
+ return !this.readonly && this.passwordSecured;
+ }
+
+ @Output() dateChange = new EventEmitter();
+ @Output() focus = new EventEmitter();
+
+ constructor(
+ public datepipe: DatePipe,
+ private applyMaskService: ApplyMaskService,
+ private cdRef: ChangeDetectorRef,
+ private msgBoxService: MsgBoxService,
+ private renderer: Renderer2,
+ private el: ElementRef,
+ private dynamicOverlayHandler: NbDynamicOverlayHandler,
+ private dateUtilsService: DateUtilsService,
+
+ ) {
+ }
+
+ ngAfterViewInit() {
+ this.renderer.removeAttribute(this.el.nativeElement, 'id');
+ setTimeout(() => {
+ this.dynamicOverlay = this.configureDynamicOverlay().build();
+ }, 400);
+
+ }
+
+ writeValue(value: Date): void {
+ if (value) {
+ this.value = value;
+ this.displayValue = this.dateUtilsService.format(this.value, DATE_FORMAT)
+ }
+ else if (value !== undefined) {
+ this.value = null;
+ this.displayValue = '';
+ }
+ this._lastBlurValue = this.value;
+ }
+
+ registerOnChange(fn: (value: Date) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState?(isDisabled: boolean): void {
+ this._disabled = isDisabled;
+ }
+
+ //#endregion
+
+ ngOnInit() {
+
+ this.dynamicOverlayHandler
+ .host(this.input)
+ .componentType(CalendarViewComponent);
+ }
+ public get displayValue(): string {
+ return this.input.nativeElement.value;
+ }
+ public set displayValue(v: string) {
+ this.input.nativeElement.value = v;
+ }
+
+ public get value(): Date {
+ return this._value;
+ }
+ public set value(v: Date) {
+
+ var changedByLimited = false;
+ if (v) {
+ //Check max and min
+ if (v < this.min) {
+ v = this.min;
+ changedByLimited = true;
+ } else if (v > this.max) {
+ v = this.max;
+ changedByLimited = true;
+ }
+ }
+
+ if (false == this.dateUtilsService.isSameDate(this._lastBlurValue, v)) {
+ this._value = v;
+ this.onChange(this.value);
+ }
+ //this.datePicker.hide();
+ if (changedByLimited) {
+ this.displayValue = this.dateUtilsService.format(v, DATE_FORMAT);
+ }
+ }
+
+ toggleCalendar() {
+ this._calendarIsShown = !this._calendarIsShown;
+ if (this._calendarIsShown) {
+ this.dynamicOverlay = this.configureDynamicOverlay().rebuild();
+ }
+ this.dynamicOverlay.toggle();
+ // if (this.datePicker.isShown) {
+
+ // this.datePicker.hide();
+ // } else {
+
+ // this.datePicker.show();
+ // }
+ }
+
+
+ setDisplayFormat() {
+
+ this.displayValue = this.applyMaskService
+ .applyMask(this.displayValue, this.maskExpression)
+
+ //this.validate();
+ }
+
+ validate(control: AbstractControl): ValidationErrors {
+
+ if (this._required && !this.value && this.displayValue.length == 0) {
+ this.invalid = true;
+ }
+ else {
+ this.invalid = !this.applyMaskService.validate(this.displayValue, this.maskExpression);
+ }
+
+ if (this.invalid && this.input.nativeElement.parentElement.className.indexOf('ng-touched') > -1) {
+ if (this.popover) this.popover.show();
+ return { key: 'Date', }
+ }
+
+ if (this.popover) this.popover.hide();
+ return null;
+ }
+
+ // triggerChange(wipeOutInvalidDate = false) {
+ // if (this.displayValue.length == this.maskExpression.length) {
+ // var newValue = new Date(this.displayValue);
+ // let isValid = this.dateUtilsService.isValidDate(newValue);
+ // if (
+ // this.value == null ||
+ // false === this.dateUtilsService.isSameDate(newValue, this.value)
+ // ) {
+ // this.value = newValue;
+ // }
+ // }
+ // else if (this.displayValue.length == 0 || wipeOutInvalidDate) {
+ // this.clearDate();
+ // }
+
+ // if (this.value != this._lastEditValue) {
+ // this._lastEditValue = this.value;
+ // this.onChange(this.value);
+ // this.dateChange.emit(this.value);
+ // }
+
+ // }
+
+
+ pickDate(date: Date) {
+ if (!this._disabled && !this._readOnly) {
+ this.displayValue = this.dateUtilsService.format(date, DATE_FORMAT);
+ this.onBlur();
+ }
+ this.hide();
+ this.input.nativeElement.focus();
+ this.dynamicOverlay = this.configureDynamicOverlay().rebuild();
+ }
+
+ unlockField() {
+ this.msgBoxService.getAuthPermission(this._passwordTag).pipe(first()).subscribe(result => {
+ if (result) {
+ this.passwordSecured = false;
+ setTimeout(() => {
+ this.setCursorPosition(0);
+ }, 100);
+ }
+ });
+ }
+
+ setCursorPosition(position) {
+ let input = this.input.nativeElement;
+ if (input.setSelectionRange) {
+ input.focus();
+ input.setSelectionRange(position, position);
+ }
+ else if (input.createTextRange) {
+ var range = input.createTextRange();
+ range.collapse(true);
+ range.moveEnd('character', position);
+ range.moveStart('character', position);
+ range.select();
+ }
+ }
+
+ private destroy$: Subject = new Subject();
+
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ this.dynamicOverlayHandler.destroy();
+ }
+
+
+
+ private configureDynamicOverlay() {
+
+
+ //.el[0].scrollTop = (i - MAX_VISIBLE_ITEMS / 2) * VISIBLE_ITEM_HEIGHT;
+
+ var windowHeight = window.innerHeight;
+ //include input box height
+ //var itemHeight = (visibleItemsCount + 1) * VISIBLE_ITEM_HEIGHT;
+ var itemHeight = 405;
+ //this.input.nativeElement.offsetHeight
+ //var y = (this.input.nativeElement.offsetHeight + itemHeight) > windowHeight ? (windowHeight - itemHeight) : this.input.nativeElement.offsetHeight;
+ var y = this.input.nativeElement ? this.input.nativeElement.getBoundingClientRect().y : 0;
+ let menuOnBottom = (y + itemHeight) < windowHeight;
+
+ return this.dynamicOverlayHandler
+ .position(menuOnBottom ? NbPosition.BOTTOM_START : NbPosition.TOP_START)
+ .trigger(NbTrigger.NOOP)
+ .offset(menuOnBottom ? 2 : -11)
+ .adjustment(NbAdjustment.NOOP)
+ .context({
+ position: menuOnBottom ? NbPosition.BOTTOM : NbPosition.TOP,
+ date: this.value,
+ class: 'text-left',
+ width: this.input.nativeElement.parentElement.clientWidth,
+ hide: () => { this.hide(); },
+ focus: () => { this.isFocused = true; },
+ dateChange: (date) => { this.pickDate(date); }
+ });
+ }
+ isFocused: boolean = false;
+ hide() {
+ this._calendarIsShown = false;
+ if (this.dynamicOverlay && !this.cdRef['destroyed']) {
+ this.dynamicOverlay.hide();
+ this.cdRef.detectChanges();
+ }
+ }
+ onBlur() {
+ this.onTouched();
+ if (false == this._calendarIsShown) {
+
+ //For quick input `07/09/21` => `07/09/2021`
+ if (this.displayValue.match(/^\d{2}\/\d{2}\/\d{2}$/g)) {
+ this.displayValue = this.displayValue.slice(0, 6) + '20' + this.displayValue.slice(6, 8);
+ } else if (this.displayValue.match(/^\d{2}\/\d{2}$/g)) {
+ this.displayValue = this.displayValue.slice(0, 6) + '20' + this.displayValue.slice(6, 8);
+ }
+ }
+ if (this.displayValue) {
+ var newValue = new Date(this.displayValue);
+ let isValid = this.displayValue.length == this.maskExpression.length && this.dateUtilsService.isValidDate(newValue);
+ if (isValid) {
+ //This is for 02/30/2022 detection, new Date('02/30/2022') will parse it as 03/02/2022
+ isValid = this.displayValue == this.dateUtilsService.format(newValue, DATE_FORMAT);
+ }
+ if (isValid) {
+ this.value = newValue;
+ } else {
+ this.msgBoxService.show('Invalid Date Input', {
+ text: `${this.displayValue} is not a valid date!`,
+ icon: ADIcon.WARNING
+ });
+ this.clearDate();
+ }
+ } else {
+ this.clearDate();
+ }
+
+ if (false == this.dateUtilsService.isSameDate(this._lastBlurValue, this.value)) {
+
+ this._lastBlurValue = this.value;
+ this.dateChange.emit(this.value);
+ }
+ }
+ private clearDate() {
+ this.value = null;
+ this.displayValue = '';
+
+ }
+}
diff --git a/APP/src/components/date-input/date-input.module.ts b/APP/src/components/date-input/date-input.module.ts
new file mode 100644
index 0000000..2eca945
--- /dev/null
+++ b/APP/src/components/date-input/date-input.module.ts
@@ -0,0 +1,35 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { DateInputComponent } from './date-input.component';
+import { NbInputModule, NbDatepickerModule, NbIconModule, NbButtonModule, NbCardModule, NbCalendarKitModule, NbTooltipModule } from '@nebular/theme';
+import { RegexValidatorModule } from '../../directives/regex-validator/regex-validator.module';
+import { FormsModule } from '@angular/forms';
+import { ForceFocusMsgModule } from '../../directives/force-focus-msg/force-focus-msg.module';
+import { CalendarViewComponent } from './calendar-view/calendar-view.component';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+import { DxCalendarModule } from 'devextreme-angular';
+
+@NgModule({
+ declarations: [DateInputComponent, CalendarViewComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ NbDatepickerModule,
+ NbIconModule,
+ NbButtonModule,
+ RegexValidatorModule,
+ NbIconModule,
+ NbButtonModule,
+ NbCardModule,
+ NbCalendarKitModule,
+ NbTooltipModule,
+ ForceFocusMsgModule,
+ MaskDirectiveModule,
+ DxCalendarModule
+ ],
+ exports: [
+ DateInputComponent
+ ]
+})
+export class DateInputModule { }
diff --git a/APP/src/components/drop-down-list/drop-down-list.component.html b/APP/src/components/drop-down-list/drop-down-list.component.html
new file mode 100644
index 0000000..ebb1c5f
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.component.html
@@ -0,0 +1,16 @@
+
\ No newline at end of file
diff --git a/APP/src/components/drop-down-list/drop-down-list.component.scss b/APP/src/components/drop-down-list/drop-down-list.component.scss
new file mode 100644
index 0000000..d4f0e6a
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.component.scss
@@ -0,0 +1,33 @@
+@import "../../@theme/styles/themes";
+
+form.ng-touched input.ng-invalid {
+ border-color: nb-theme(color-danger-default);
+}
+.dropdownBtn {
+ width: 25px !important;
+ padding: 0 !important;
+}
+
+[nbInput]:disabled {
+ background-color: white !important;
+}
+
+input {
+ cursor: pointer;
+ text-overflow: ellipsis;
+ .dropdownInput {
+ border-top-right-radius: 0 !important;
+ border-bottom-right-radius: 0 !important;
+ min-width: 25px;
+ height: auto;
+ }
+ .editable {
+ cursor: text;
+ }
+ &:read-only:not(.editable) {
+ cursor: pointer;
+ }
+ &.upperFirstLetter {
+ text-transform: capitalize;
+ }
+}
diff --git a/APP/src/components/drop-down-list/drop-down-list.component.spec.ts b/APP/src/components/drop-down-list/drop-down-list.component.spec.ts
new file mode 100644
index 0000000..3069d83
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { DropDownListComponent } from './drop-down-list.component';
+
+describe('DropDownListComponent', () => {
+ let component: DropDownListComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DropDownListComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DropDownListComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/drop-down-list/drop-down-list.component.ts b/APP/src/components/drop-down-list/drop-down-list.component.ts
new file mode 100644
index 0000000..6d16cd5
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.component.ts
@@ -0,0 +1,699 @@
+import { Component, Input, Output, EventEmitter, forwardRef, ViewChild, ElementRef, ChangeDetectionStrategy, ChangeDetectorRef, Renderer2, HostListener, ViewChildren, QueryList } from '@angular/core';
+import { AbstractControl, ControlValueAccessor, NG_VALIDATORS, NG_VALUE_ACCESSOR, ValidationErrors, Validator } from '@angular/forms';
+import { DropDownOption } from '../../entity/dropDownOption';
+import { NbContextMenuDirective, NbDynamicOverlay, NbDynamicOverlayHandler, NbTrigger, NbAdjustment, NbPosition, NbTooltipDirective } from '@nebular/theme';
+import { filter, map, takeUntil } from 'rxjs/operators';
+import { trigger, state, transition, animate, style } from '@angular/animations';
+import { Subject } from 'rxjs';
+import { DropDownMenuComponent } from './drop-down-menu/drop-down-menu.component';
+import { UuidUtils } from '../../utilities/uuid-utils';
+import { DropDownListService } from './drop-down-list.service';
+import { StringUtils } from '../../utilities/string-utils';
+import { DebounceTimer } from '../../utilities/timer-utils';
+import { RbjTooltipService } from '../../services/rbj-tooltip.service';
+import { TextareaUtils } from '../../utilities/textarea-utils';
+
+const MAX_VISIBLE_ITEMS = 8;
+const VISIBLE_ITEM_HEIGHT = 40;
+const TEXT_CLEAR = "Clear The Field";
+
+@Component({
+ selector: 'rbj-drop-down',
+ templateUrl: './drop-down-list.component.html',
+ styleUrls: ['./drop-down-list.component.scss'],
+ animations: [
+ trigger('openClose', [
+ // ...
+ state('open', style({
+ transform: 'rotate(180deg)'
+ })),
+ state('closed', style({
+ transform: 'rotate(0deg)'
+ })),
+ transition('* => *', [
+ animate(100)
+ ]),
+ ]),
+ ],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => DropDownListComponent),
+ multi: true
+ },
+ {
+ provide: NG_VALIDATORS,
+ useExisting: forwardRef(() => DropDownListComponent),
+ multi: true,
+ },
+ NbDynamicOverlayHandler,
+ NbDynamicOverlay
+ ]
+})
+export class DropDownListComponent implements ControlValueAccessor, Validator {
+
+ @ViewChild('inputBox', { static: true }) input: ElementRef;
+ @ViewChild('dropButton', { static: true }) button: ElementRef;
+ @ViewChild(NbContextMenuDirective, { static: true }) contextMenu: NbContextMenuDirective;
+
+ refreshMenuDebounceTimer = new DebounceTimer(20, () => { if (this.menuIsOpen) { this.setUpDropdownList(); } });
+ constructor(
+ private dropDownListService: DropDownListService,
+ private cdRef: ChangeDetectorRef,
+ private dynamicOverlayHandler: NbDynamicOverlayHandler,
+ private elementRef: ElementRef,
+ private renderer: Renderer2,
+ private rbjTooltipService: RbjTooltipService,
+ ) {
+ this.tabindex = elementRef.nativeElement.getAttribute('tabindex');
+ }
+
+ private destroy$: Subject = new Subject();
+ private _initialized: boolean;
+ private _viewInitialized: boolean = false;
+ private _value: any;
+ private _text: string;
+ private _textTyping: boolean = false;
+ private dynamicOverlay: NbDynamicOverlay;
+ private isFocused: boolean = false;
+ private _propDisplay: string = 'value1';
+
+ status = '';
+ displayValue: string;
+ selectedItem: DropDownOption;
+ firstMatchOption: DropDownOption;
+ uuid = 'rbjDropDown';
+ items: DropDownOption[] = [];
+
+ readonly: boolean = false;
+ required: boolean = false;
+ mustMatch: boolean = true;
+ @Input() allowSearch: boolean = true;
+ compact: boolean
+ menuIsOpen: boolean = false;
+ selectedIndex: number = -1;
+ tabindex: string = null;
+ focusIndex: number = -1;
+ usingFocusIndex: boolean = false;
+
+ public get tooltip(): string {
+ return this.selectedItem ? this.selectedItem[this.propItemDisplay] || '' : '';
+ }
+
+ public get value(): any {
+ return this._value;
+ }
+ public set value(v: any) {
+ if (this._initialized) {
+ if (v != this._value) {
+ this._value = v;
+ this.onChange(this.value);
+ }
+ }
+ else {
+ this._initialized = true;
+ this._value = v;
+ }
+ this.keyValueChangedByBinding();
+ }
+
+ @Input()
+ public get text(): string {
+ return this._text;
+ }
+ public set text(v: string) {
+ this.input.nativeElement.value = v;
+ if (v != this._text) {
+
+ this._text = v;
+ this.textChange.emit(this._text);
+ }
+ }
+
+ @Input() public set source(v: DropDownOption[]) {
+ if (this._source != v) {
+ this._source = v;
+ this.refreshMenuDebounceTimer.resetTimer();
+ //if (this.menuIsOpen) { this.setUpDropdownList(); }
+ this.keyValueChangedByBinding();
+ }
+ }
+ private _source: DropDownOption[];
+ public get source(): DropDownOption[] {
+ return this._source;
+ }
+
+ public get availableOptions(): DropDownOption[] {
+ if (this.source) {
+ return this.source.filter(o => this.itemIsVisible(o) && !o[this.propDisabled]);
+ }
+ return [];
+ }
+
+ @Input() name?: string = ''
+ @Input() id?: string = ''
+ @Input() hideButton: boolean = false;
+ @Input() inputClass = '';
+
+ public get propDisplay(): string {
+ return this._propDisplay;
+ }
+
+ /**
+ * Display text of dropdown will displaying this property of dropdown item, defaults as `value1`, will set `propItemDisplay` also, if it's default.
+ */
+ @Input() public set propDisplay(v: string) {
+ this._propDisplay = v;
+ if (this.propItemDisplay == 'value1') this.propItemDisplay = v;
+ }
+
+ /**
+ * Display text of dropdown context menu will displaying this property of dropdown item, support HTML code, defaults as `value1`
+ */
+ @Input() propItemDisplay = 'value1';
+ /**
+ * The `key` property name of dropdown item, defaults as `key`
+ */
+ @Input() propKey = 'key';
+
+ /**
+ * The `disabled` property name of dropdown item, defaults as `disabled`
+ */
+ @Input() propDisabled = 'disabled';
+ /**
+ * The `visible` property name of dropdown item, defaults as `visible`
+ */
+ @Input() propVisible = 'visible';
+ @Input() matchWith = 'key';
+ @Input() disabled = false;
+ @Input() placeholder = '';
+ @Input() maskExpression = null;
+ @Input() clearOption = false;
+
+ /**
+ * Tooltip message for no matching found, default as "$inputText Not Matched", `$inputText` will replace with user input text
+ */
+ @Input() inputNotMatchedTooltip = "$inputText does not exist";
+
+ @HostListener('focus')
+ focusHandler() {
+ this.focus();
+ }
+
+ @Input("mustMatch")
+ public set input_mustMatch(value) {
+ this.mustMatch = typeof value !== "undefined" && value !== false;
+ }
+
+ @Input("required")
+ public set input_required(value) {
+ this.required = typeof value !== "undefined" && value !== false;
+ }
+ @Input("readonly")
+ public set input_readonly(value) {
+ this.readonly = typeof value !== "undefined" && value !== false;
+ }
+ @Input("compact")
+ public set input_compact(value) {
+ this.compact = typeof value !== "undefined" && value !== false;
+ }
+ @Input("clearOption")
+ public set input_clearOption(value) {
+ this.clearOption = typeof value !== "undefined" && value !== false;
+ }
+
+ @Output() selectedChange = new EventEmitter();
+ @Output() selectedChangeFrom = new EventEmitter<{ previousValue: any, currentValue: any }>();
+ @Output() menuItemSelected = new EventEmitter();
+ @Output() textChange = new EventEmitter();
+ @Output() blur: EventEmitter = new EventEmitter();
+ @Output() inputTextNotMatched = new EventEmitter();
+
+ /**
+ * Triggering when dropdown option been change by user, when subscribe this will not trigger selectedChange any more
+ */
+ @Output() beforeChange = new EventEmitter();
+
+ onChange = (value: any) => { };
+ onTouched = () => { };
+
+ writeValue(value: any): void {
+ this.value = value;
+ }
+
+ registerOnChange(fn: (value: any) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState?(isDisabled: boolean): void {
+ this.disabled = isDisabled;
+ }
+
+ validate(control: AbstractControl): ValidationErrors {
+ if (this.required && (this.value == null)) {
+ return { "dropdown": "" };
+ }
+ return null;
+ }
+
+ registerOnValidatorChange?(fn: () => void): void {
+ }
+
+ ngOnInit() {
+ this.uuid += UuidUtils.generate();
+
+ this.dropDownListService.onItemSelect
+ .pipe(
+ takeUntil(this.destroy$),
+ filter(({ tag }) => tag === this.uuid),
+ map(({ item }) => item),
+ )
+ .subscribe(item => {
+ this.selectByMenu(item);
+ });
+
+ this.dynamicOverlayHandler
+ .host(this.input)
+ .componentType(DropDownMenuComponent);
+ }
+
+ ngAfterViewInit() {
+ this._viewInitialized = true;
+ this.setUpDropdownList();
+ this.dynamicOverlay = this.configureDynamicOverlay().build();
+ this.renderer.removeAttribute(this.elementRef.nativeElement, 'id')
+ }
+ ngOnChanges() {
+ //this.dynamicOverlay = this.configureDynamicOverlay().rebuild();
+ }
+
+ ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ this.dynamicOverlayHandler.destroy();
+ }
+
+ private itemIsVisible(item: any) {
+ if (item[this.propVisible] == undefined) {
+ item[this.propVisible] = true;
+ }
+
+ return true === item[this.propVisible];
+ }
+
+ focusout() {
+ this.isFocused = false;
+ }
+
+ onFocus() {
+ this.isFocused = true;
+ }
+
+ onTextFocus() {
+ this.isFocused = true;
+ this._textTyping = true;
+ }
+
+ onTextFocusout() {
+ this._textTyping = false;
+ this.focusout();
+ }
+
+ focus() {
+ this.input.nativeElement.focus();
+ }
+
+ /**
+ * Remove `ng-invalid` class from input field, only calling this method for data been changed in background but UI doesn't refresh.
+ */
+ private removeInvalidClassFormInput() {
+ this.renderer.removeClass(this.input.nativeElement, 'ng-invalid');
+ }
+ updateTextFromValue() {
+ if (this.clearOption && StringUtils.isNullOrWhitespace(this.value)) {
+ this.text = '';
+ this.input.nativeElement.value = "";
+ }
+ else if (this.source) {
+ let item: DropDownOption = null;
+
+ if (null == this.value && this.mustMatch) {
+ item = this.availableOptions.find(o => o.default);
+ }
+ else {
+ item = this.availableOptions.find(o => o[this.propKey] == this.value);
+ }
+
+ this.selectedIndex = item ? this.source.indexOf(item) : -1;
+ this._text = item ? item[this.propDisplay] : this.value;
+
+ if (this.input) {
+ this.input.nativeElement.value = this._text;
+ this.removeInvalidClassFormInput();
+ }
+ }
+ else {
+ this.text = '';
+ }
+ this.cdRef.detectChanges();
+ }
+
+
+ clickSelectAllText() {
+ if (this.allowSearch) {
+ this.input.nativeElement.select();
+ }
+ }
+
+ onTextFieldBlur() {
+
+ //set timeout for avoiding menu been hided before click event triggered
+ // setTimeout(() => {
+ this.updateSelectedItemByText();
+ this.onTouched();
+ this.blur.next(this.text);
+ // }, 200);
+ }
+
+ private setUpDropdownList() {
+ this.items = [];
+ if (this.source && this.source.length > 0) {
+ if (this.clearOption) {
+ this.items = [new DropDownOption('', '')];
+ this.items[0][this.propKey] = "";
+ this.items[0][this.propDisplay] = "";
+ this.items[0][this.propItemDisplay] = TEXT_CLEAR;
+ }
+
+ if (this.menuIsOpen && this.allowSearch && this.input.nativeElement) {
+ let filter = this.input.nativeElement.value.toLowerCase();
+ this.items = this.items.concat(
+ this.availableOptions.filter(s => s[this.propItemDisplay].toLowerCase().indexOf(filter) > -1
+ ));
+
+ //unique by this.propItemDisplay
+ this.items = Array.from(new Map(this.items.map(item =>
+ [item[this.propItemDisplay], item])).values());
+
+ if (this.items.length > 0) {
+ this.firstMatchOption = this.availableOptions.find(s => s[this.propKey] == this.items[0][this.propKey]);
+ this.firstMatchOption = this.items[this.focusIndex];
+ }
+ else {
+ this.firstMatchOption = null;
+ }
+ }
+ else {
+ this.items = this.items.concat(this.source)
+ }
+
+ if (this.selectedItem) {
+ this.focusIndex = this.items.indexOf(this.selectedItem)
+ this.focusIndex = this.focusIndex > -1 ? this.focusIndex : 0;
+ }
+
+ this.usingFocusIndex = false;
+ }
+
+ if (this._viewInitialized) {
+
+ this.dynamicOverlay = this.configureDynamicOverlay()
+ .rebuild();
+ }
+ }
+
+ private configureDynamicOverlay() {
+ let visibleItemsCount = this.items.filter(i => i.visible).length;
+ visibleItemsCount = visibleItemsCount < MAX_VISIBLE_ITEMS ? visibleItemsCount : MAX_VISIBLE_ITEMS;
+ var windowHeight = window.innerHeight;
+ //include input box height
+ var itemHeight = (visibleItemsCount + 1) * VISIBLE_ITEM_HEIGHT;
+ var y = this.input.nativeElement ? this.input.nativeElement.getBoundingClientRect().y : 0;
+ let menuOnBottom = (y + itemHeight) < windowHeight;
+
+ return this.dynamicOverlayHandler
+ .position(menuOnBottom ? NbPosition.BOTTOM_END : NbPosition.TOP_END)
+ .trigger(NbTrigger.NOOP)
+ .offset(menuOnBottom ? 2 : -40)
+ .adjustment(NbAdjustment.NOOP)
+ .context({
+ position: menuOnBottom ? NbPosition.BOTTOM : NbPosition.TOP,
+ items: this.items,
+ tag: this.uuid,
+ class: 'text-left',
+ width: this.input.nativeElement.parentElement.clientWidth,
+ propDisplay: this.propItemDisplay,
+ propDisabled: this.propDisabled,
+ focusIndex: this.focusIndex,
+ hide: () => { this.hide(); },
+ focus: () => { this.isFocused = true; }
+ });
+ }
+
+ private updateSelectedItemInMenu() {
+ this.items.forEach(item => {
+ item.selected = item[this.propKey] == this.value;
+ });
+ }
+
+ private show() {
+ this.menuIsOpen = true;
+ this.dynamicOverlay.show();
+ this.cdRef.detectChanges();
+ }
+
+ private hide() {
+ this.menuIsOpen = false;
+ if (this.dynamicOverlay && !this.cdRef['destroyed']) {
+ this.dynamicOverlay.hide();
+ this.cdRef.detectChanges();
+ }
+ }
+
+ toggle(selectAllText: boolean = true) {
+ if (!this.disabled && !this.readonly) {
+ if (false == this.menuIsOpen) {
+ this.setUpDropdownList();
+ }
+ this.dynamicOverlay.toggle();
+ if (this.dynamicOverlay.isAttached) {
+ this.updateSelectedItemInMenu();
+
+ this.scrollingMenuByIndex(this.selectedIndex);
+
+ this.menuIsOpen = true;
+ if (selectAllText) {
+ this.clickSelectAllText();
+ }
+ }
+ else {
+ this.menuIsOpen = false;
+ }
+ }
+ }
+
+ private scrollingMenuByIndex(i: number) {
+ if (this.dynamicOverlay && this.dynamicOverlay.isAttached) {
+ if (this.items.length > MAX_VISIBLE_ITEMS) {
+ const el = document.getElementsByClassName(this.uuid);
+ el[0].scrollTop = (i - MAX_VISIBLE_ITEMS / 2) * VISIBLE_ITEM_HEIGHT;
+ }
+ }
+ }
+
+ //Triggered when user clicks option from dropdown menu
+ private selectByMenu(item: DropDownOption) {
+ if (this.beforeChange.observers.length == 0) {
+ this.menuItemSelected.emit(item[this.propKey]);
+
+ if (this.value != item[this.propKey]) {
+ const previousValue = this.value;
+ this.value = item[this.propKey]
+ this._text = item[this.propDisplay] == TEXT_CLEAR ? '' : item[this.propDisplay];
+
+ //this line is for triggering html input field validating without triggering whole textChange process
+ this.removeInvalidClassFormInput();
+ this.selectedIndex = this.items.indexOf(item);
+ this.blur.next(this.text);
+ this.textChange.next(this.text);
+ this.onChange(this.value);
+ this.selectedChange.emit(this.value);
+ this.selectedChangeFrom.emit({ previousValue, currentValue: this.value });
+
+ if (this.hideButton) {
+ this.input.nativeElement.focus();
+ }
+ else {
+ this.input.nativeElement.focus();
+ this.clickSelectAllText();
+ }
+
+ this.onTouched();
+ this.updateSelectedItemInMenu();
+ }
+ }
+ else {
+ this.beforeChange.emit(item[this.propKey]);
+ }
+ }
+
+ //Key value changed from binding, will update the displaying text and selected item in menu.
+ private keyValueChangedByBinding() {
+ let defaultOption = null;
+ if (this.source) {
+
+ let comparison = this._value;
+ this.selectedItem = this.availableOptions.find(o => o[this.propKey] == comparison);
+ defaultOption = this.availableOptions.find(o => o.default);
+ }
+ if (!this.selectedItem) {
+ if (this.mustMatch) {
+ if (defaultOption) {
+ this._value = defaultOption[this.propKey];
+ this.selectedItem = defaultOption;
+ }
+ else {
+ this._value = null;
+ this.selectedItem = new DropDownOption('', '');
+ }
+ }
+ this.onChange(this.value);
+ }
+
+ //this.text = this.selectedItem[this.propDisplay];
+ this.updateTextFromValue();
+ }
+ private updateSelectedItemByText() {
+ this._text = this.input.nativeElement.value;
+ let originSelectedItem = this.selectedItem;
+ if (!this.readonly && this.source && this.allowSearch) {
+ var newValue = null;
+ var inputText = StringUtils.toUpperString(this.text);
+ var foundByInput = this.availableOptions.find(o => StringUtils.toUpperString(o[this.propDisplay]) == inputText);
+
+ //Find again with Key Value
+ if (!foundByInput) {
+ foundByInput = this.availableOptions.find(o => StringUtils.toUpperString(o[this.matchWith]) == inputText);
+ }
+
+ if (this.usingFocusIndex || (!foundByInput && this.menuIsOpen && this.items.length > 0)) {
+ foundByInput = this.items[this.focusIndex];
+ //this.firstMatchOption = null;
+ }
+
+ if (!foundByInput) {
+ if (this.mustMatch) {
+ //Reset text to current value if mustMatch is true and no result
+ this.textInputNoMatched(this.text);
+ inputText = this.selectedItem[this.propDisplay];
+ }
+ else {
+ inputText = this.text;
+ newValue = inputText;
+ //Set inputText as option to selected item
+ this.selectedItem = new DropDownOption(newValue, inputText);
+ this.selectedIndex = -1;
+ }
+ }
+ else {
+ //Have matched option with text input
+ this.selectedItem = foundByInput;
+ this.selectedIndex = this.source.indexOf(this.selectedItem);
+ newValue = this.selectedItem[this.propKey];
+ inputText = this.selectedItem[this.propDisplay];
+ }
+
+ if ((newValue != null || false == this.mustMatch) && newValue != this.value) {
+
+ if (this.beforeChange.observers.length == 0) {
+
+ let previousValue = this.value;
+ this.value = newValue;
+ this.selectedChange.emit(this.value);
+ this.selectedChangeFrom.emit({ previousValue, currentValue: this.value });
+ }
+ else {
+ this.beforeChange.emit(newValue);
+ }
+ }
+
+ if (this.beforeChange.observers.length == 0) {
+ if (!this.clearOption || inputText != TEXT_CLEAR) {
+ this.text = inputText;
+ }
+ }
+ else {
+ this.selectedItem = originSelectedItem;
+ this.text = this.selectedItem[this.propDisplay];
+ }
+ }
+ }
+
+ private textInputNoMatched(inputValue: string) {
+ this.inputTextNotMatched.next(this.text);
+ if (false == StringUtils.isNullOrWhitespace(this.text)) {
+ let tempMsg = this.inputNotMatchedTooltip.replace("$inputText", inputValue.toUpperCase());
+ //this.tooltips.first.content = `"${inputValue}" does not exist`;
+ this.rbjTooltipService.show(this.input, tempMsg, this.uuid, 3, NbPosition.TOP_START);
+
+ }
+ }
+
+ public onKeyDown(e: KeyboardEvent): void {
+ switch (e.key) {
+ case 'Enter':
+ this.updateSelectedItemByText();
+ this.hide();
+ return;
+ break;
+
+ case 'ArrowDown':
+ case 'ArrowUp':
+ if (this.menuIsOpen) {
+ if (e.key == "ArrowUp") {
+ this.focusIndex = this.focusIndex > 0 ? this.focusIndex - 1 : this.focusIndex;
+ }
+ else {
+ this.focusIndex = this.focusIndex == this.items.length - 1 ? this.focusIndex : this.focusIndex + 1
+ }
+
+ this.dropDownListService.setFocusIndex(this.uuid, this.focusIndex);
+ this.scrollingMenuByIndex(this.focusIndex);
+ this.usingFocusIndex = true;
+ }
+ else {
+ this.toggle();
+ }
+ return;
+ break;
+
+ case 'Tab':
+ this.isFocused = false;
+ //use time out to let tab out support select the last index user choose from menu
+ setTimeout(() => {
+ this.hide();
+ }, 100);
+ return;
+
+ break;
+
+ default:
+ break;
+ }
+ if (TextareaUtils.isEditingKeyPress(e)) {
+ if (this.allowSearch) {
+ if (this.isFocused && false == this.menuIsOpen) {
+ this.toggle(false);
+ }
+ }
+ this.refreshMenuDebounceTimer.resetTimer();
+ }
+ }
+
+}
+
diff --git a/APP/src/components/drop-down-list/drop-down-list.module.ts b/APP/src/components/drop-down-list/drop-down-list.module.ts
new file mode 100644
index 0000000..f535eb6
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.module.ts
@@ -0,0 +1,32 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { DropDownListComponent } from './drop-down-list.component';
+import { FormsModule } from '@angular/forms';
+import { NbInputModule, NbSelectModule, NbMenuModule, NbContextMenuModule, NbButtonModule, NbIconModule, NbCardModule, NbTooltipModule } from '@nebular/theme';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+import { DropDownMenuComponent } from './drop-down-menu/drop-down-menu.component';
+
+
+@NgModule({
+ declarations: [
+ DropDownListComponent,
+ DropDownMenuComponent,
+ ],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ NbSelectModule,
+ NbMenuModule,
+ NbContextMenuModule,
+ NbButtonModule,
+ NbIconModule,
+ NbCardModule,
+ NbTooltipModule,
+ MaskDirectiveModule
+ ],
+ exports: [
+ DropDownListComponent,
+ ]
+})
+export class DropDownListModule { }
diff --git a/APP/src/components/drop-down-list/drop-down-list.service.spec.ts b/APP/src/components/drop-down-list/drop-down-list.service.spec.ts
new file mode 100644
index 0000000..5c90191
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.service.spec.ts
@@ -0,0 +1,12 @@
+import { TestBed } from '@angular/core/testing';
+
+import { DropDownListService } from './drop-down-list.service';
+
+describe('DropDownListService', () => {
+ beforeEach(() => TestBed.configureTestingModule({}));
+
+ it('should be created', () => {
+ const service: DropDownListService = TestBed.get(DropDownListService);
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/drop-down-list/drop-down-list.service.ts b/APP/src/components/drop-down-list/drop-down-list.service.ts
new file mode 100644
index 0000000..2c70db2
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-list.service.ts
@@ -0,0 +1,24 @@
+import { Injectable } from '@angular/core';
+import { Observable, Subject } from 'rxjs';
+import { DropDownOption } from '../../entity/dropDownOption';
+import { DropDownBag } from './drop-down-menu/drop-down-menu.component';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class DropDownListService {
+
+ constructor() { }
+ onItemSelect = new Subject();
+
+ focusIndexChange = new Subject();
+
+
+ selectItem(tag: string, item: DropDownOption) {
+ this.onItemSelect.next({ tag: tag, item: item } as DropDownBag)
+ }
+ setFocusIndex(tag: string, i: number) {
+ this.focusIndexChange.next(
+ { tag: tag, focusIndex: i } as DropDownBag);
+ }
+}
diff --git a/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.html b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.html
new file mode 100644
index 0000000..6bbf244
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.html
@@ -0,0 +1,15 @@
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.scss b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.scss
new file mode 100644
index 0000000..3894851
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.scss
@@ -0,0 +1,87 @@
+@import "../../../@theme/styles/themes";
+
+@include nb-install-component() {
+ .context-menu {
+ background-color: nb-theme(background-basic-color-1);
+ box-shadow: nb-theme(shadow);
+ }
+
+ ul {
+ display: block;
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ li {
+ font-size: 0.9375rem;
+ font-weight: 600;
+ line-height: 1.5rem;
+ padding: 0.4375rem 1rem;
+ cursor: pointer;
+ &.focus {
+ background-color: nb-theme(color-basic-transparent-300);
+ color: nb-theme(text-basic-color); //#222b45
+ outline: none;
+ }
+ &:hover {
+ background-color: nb-theme(color-basic-transparent-200);
+ color: nb-theme(text-basic-color);
+ }
+ &.selected {
+ background-color: nb-theme(color-primary-500);
+ color: nb-theme(text-control-color);
+ }
+ &:hover.selected {
+ background-color: nb-theme(color-primary-400);
+ color: nb-theme(text-control-color);
+ }
+ &.disabled {
+ color: nb-theme(text-disabled-color) !important;
+ }
+ }
+ }
+}
+:host {
+ border: 0 solid transparent;
+ border-radius: 0.25rem;
+ //box-shadow: 0 0.5rem 1rem 0 rgba(44, 51, 73, 0.1);
+ color: #192038;
+ font-family: Open Sans, sans-serif;
+ font-size: 0.9375rem;
+ font-weight: 400;
+ line-height: 1.25rem;
+ margin-bottom: 1.875rem;
+ scrollbar-face-color: #e4e9f2;
+ scrollbar-track-color: #f7f9fc;
+ .context-menu {
+ -webkit-animation-name: fadeFromTop; /* Chrome, Safari, Opera */
+ -webkit-animation-duration: 0.25s; /* Chrome, Safari, Opera */
+ animation-name: fadeFromTop;
+ animation-duration: 0.25s;
+ max-height: 300px;
+ overflow-y: auto !important;
+ }
+}
+
+/* Add animation (Chrome, Safari, Opera) */
+@-webkit-keyframes fadeFromTop {
+ from {
+ max-height: 0px;
+ }
+ to {
+ max-height: 300px;
+ }
+}
+
+/* Add animation (Standard syntax) */
+@keyframes fadeFromTop {
+ from {
+ max-height: 0px;
+ }
+ to {
+ max-height: 300px;
+ }
+}
+
+// .rbjDropdownMenuItem:empty::before{
+// content: "\200b"; /* unicode zero width space character */
+// }
\ No newline at end of file
diff --git a/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.spec.ts b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.spec.ts
new file mode 100644
index 0000000..375b898
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { DropDownMenuComponent } from './drop-down-menu.component';
+
+describe('DropDownMenuComponent', () => {
+ let component: DropDownMenuComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DropDownMenuComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DropDownMenuComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.ts b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.ts
new file mode 100644
index 0000000..d381981
--- /dev/null
+++ b/APP/src/components/drop-down-list/drop-down-menu/drop-down-menu.component.ts
@@ -0,0 +1,103 @@
+import { Component, OnInit, Input, HostListener, Renderer2 } from '@angular/core';
+import { NbPositionedContainerComponent, NbRenderableContainer, NbMenuItem } from '@nebular/theme';
+import { Subscription } from 'rxjs';
+import { filter, map } from 'rxjs/operators';
+import { DropDownOption } from '../../../entity/dropDownOption';
+import { DropDownListService } from '../drop-down-list.service';
+
+@Component({
+ selector: 'ngx-drop-down-menu',
+ templateUrl: './drop-down-menu.component.html',
+ styleUrls: ['./drop-down-menu.component.scss']
+})
+export class DropDownMenuComponent extends NbPositionedContainerComponent implements NbRenderableContainer {
+
+ // [style.width.px]="clientWidth"
+
+ private _context: menuSetting;
+ private _indexSubscription: Subscription;
+ clientWidth: number = 0;
+ items: DropDownOption[] = [];
+ private listener: any;
+ constructor(private dropDownListService: DropDownListService,
+ private renderer: Renderer2) {
+ super();
+
+ }
+
+ ngOnInit() {
+ setTimeout(() => {
+ this.listener = this.renderer.listen('window', 'click', (e: Event) => {
+ //
+ var element = (e.target as HTMLElement);
+ if (typeof element.className === 'string' && element.className.indexOf('rbjDropdownMenu') > -1) {
+ this.context.focus();
+ } else {
+ this.context.hide();
+ this.listener();
+ }
+ })
+ }, 50);
+
+ this._indexSubscription = this.dropDownListService.focusIndexChange
+
+ .pipe(
+ filter(({ tag }) => tag === this._context.tag),
+ map(({ focusIndex }) => focusIndex),
+ )
+ .subscribe(i => {
+ this._context.focusIndex = i;
+ })
+ }
+ ngOnDestroy() {
+ if (this.listener) { this.listener(); }
+ if (this._indexSubscription) {
+
+ this._indexSubscription.unsubscribe();
+ }
+ }
+ public get context(): menuSetting {
+ return this._context;
+ }
+ @Input() public set context(v: menuSetting) {
+ if (this._context != v) {
+ this._context = v;
+ this.clientWidth = document.getElementsByClassName(`div${this._context.tag}`)[0].clientWidth;
+ this.items = v.items;
+ }
+ }
+
+ @HostListener('focus')
+ focus() {
+ this.context.focus();
+ }
+
+ renderContent() {
+
+ }
+
+ choose(item: DropDownOption) {
+
+ if (!item[this.context.propDisabled]) {
+ this.dropDownListService.selectItem(this.context.tag, item);
+ this.context.hide();
+ }
+ }
+
+}
+export interface menuSetting {
+ items: DropDownOption[],
+ tag?: string,
+ class?: string,
+ propDisplay: string,
+ propDisabled: string,
+ focusIndex: number
+ hide: () => {},
+ focus: () => {}
+}
+export interface DropDownBag {
+ tag: string,
+ item: DropDownOption,
+ focusIndex?: number
+}
+
diff --git a/APP/src/components/email-input/email-input.component.html b/APP/src/components/email-input/email-input.component.html
new file mode 100644
index 0000000..8b12bb0
--- /dev/null
+++ b/APP/src/components/email-input/email-input.component.html
@@ -0,0 +1,3 @@
+
\ No newline at end of file
diff --git a/APP/src/components/email-input/email-input.component.scss b/APP/src/components/email-input/email-input.component.scss
new file mode 100644
index 0000000..2011e7d
--- /dev/null
+++ b/APP/src/components/email-input/email-input.component.scss
@@ -0,0 +1,4 @@
+:host {
+ flex: auto;
+ display: contents;
+}
diff --git a/APP/src/components/email-input/email-input.component.spec.ts b/APP/src/components/email-input/email-input.component.spec.ts
new file mode 100644
index 0000000..0f90842
--- /dev/null
+++ b/APP/src/components/email-input/email-input.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { EmailInputComponent } from './email-input.component';
+
+describe('EmailInputComponent', () => {
+ let component: EmailInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ EmailInputComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(EmailInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/email-input/email-input.component.ts b/APP/src/components/email-input/email-input.component.ts
new file mode 100644
index 0000000..e35232d
--- /dev/null
+++ b/APP/src/components/email-input/email-input.component.ts
@@ -0,0 +1,74 @@
+import { Component, OnInit, ChangeDetectionStrategy, forwardRef, Input, Output, EventEmitter, ViewChild, ElementRef, Renderer2 } from '@angular/core';
+import { NG_VALUE_ACCESSOR, ControlContainer, NgForm, DefaultValueAccessor } from '@angular/forms';
+import { UuidUtils } from '../../utilities/uuid-utils';
+
+@Component({
+ selector: 'rbj-email-input',
+ templateUrl: './email-input.component.html',
+ styleUrls: ['./email-input.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => EmailInputComponent),
+ multi: true
+ }
+ ],
+ viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
+})
+export class EmailInputComponent implements OnInit {
+
+ private _value: string;
+ disabledState: boolean = false;
+ readOnly: boolean = false;
+
+ @Input()
+ public get value(): string {
+ return this._value;
+ }
+
+ onChange = (value: string) => { };
+ onTouched = () => { };
+
+ uuid: string = UuidUtils.generate();
+ @Input() id?= "";
+ @Input() placeholder: string;
+ @Input() class: string;
+ @Input() size: string = 'medium';
+ public set value(v: string) {
+ if (this._value != v) {
+ this._value = v;
+ this.onChange(v)
+ }
+ }
+
+ @Input("readonly")
+ public set input_readOnly(value) {
+ this.readOnly = typeof value !== "undefined" && value !== false;
+ }
+ @ViewChild('inputBox', { static: true }) input: ElementRef;
+
+ constructor(
+ private renderer: Renderer2,
+ private el: ElementRef) { }
+
+ ngAfterViewInit() {
+ this.renderer.removeAttribute(this.el.nativeElement, 'id')
+ }
+ writeValue(value: any): void {
+ this.value = value;
+ }
+
+ registerOnChange(fn: (value: string) => void): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState(isDisabled: boolean): void {
+ this.disabledState = isDisabled;
+ }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/APP/src/components/email-input/email-input.module.ts b/APP/src/components/email-input/email-input.module.ts
new file mode 100644
index 0000000..a786106
--- /dev/null
+++ b/APP/src/components/email-input/email-input.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { EmailInputComponent } from './email-input.component';
+import { NbInputModule } from '@nebular/theme';
+import { RegexValidatorModule } from '../../directives/regex-validator/regex-validator.module';
+import { FormsModule } from '@angular/forms';
+import { RbjTooltipModule } from '../../directives/rbj-tooltip/rbj-tooltip.module';
+
+@NgModule({
+ declarations: [EmailInputComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ RegexValidatorModule,
+ RbjTooltipModule
+ ],
+ exports: [EmailInputComponent]
+})
+export class EmailInputModule { }
diff --git a/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.html b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.html
new file mode 100644
index 0000000..8c2f804
--- /dev/null
+++ b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.html
@@ -0,0 +1,9 @@
+
+ {{ title }}
+
+
+ Delete
+ Cancel
+
+
+
diff --git a/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.scss b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.scss
new file mode 100644
index 0000000..5a47eb9
--- /dev/null
+++ b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.scss
@@ -0,0 +1,25 @@
+@import './../../../@theme/styles/themes';
+
+@include nb-install-component() {
+ .buttons-row {
+ margin: -0.5rem;
+ }
+
+ button[nbButton] {
+ margin: 0.5rem;
+ }
+
+ .action-icon {
+ @include nb-ltr(margin-right, 0.5rem);
+ @include nb-rtl(margin-left, 0.5rem);
+ }
+
+ .actions-card {
+ height: 8rem;
+ }
+
+ nb-card {
+ max-width: 600px;
+ max-height: 500px;
+ }
+}
diff --git a/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.spec.ts b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.spec.ts
new file mode 100644
index 0000000..384fdf3
--- /dev/null
+++ b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { ConfirmDialogComponent } from './confirm-dialog.component';
+
+describe('ConfirmDialogComponent', () => {
+ let component: ConfirmDialogComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ConfirmDialogComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ConfirmDialogComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.ts b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.ts
new file mode 100644
index 0000000..e3f4dc2
--- /dev/null
+++ b/APP/src/components/fancy-table/confirm-dialog/confirm-dialog.component.ts
@@ -0,0 +1,24 @@
+import { Component, Input, Output } from '@angular/core';
+import { NbDialogRef } from '@nebular/theme';
+
+@Component({
+ selector: 'ngx-confirm-dialog',
+ templateUrl: './confirm-dialog.component.html',
+ styleUrls: ['./confirm-dialog.component.scss']
+})
+export class ConfirmDialogComponent {
+
+ @Input() title: string;
+
+ constructor(
+ protected ref: NbDialogRef,
+ ) { }
+
+ cancel() {
+ this.ref.close();
+ }
+
+ delete(confirm) {
+ this.ref.close(confirm);
+ }
+}
diff --git a/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.html b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.html
new file mode 100644
index 0000000..df92ed7
--- /dev/null
+++ b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.html
@@ -0,0 +1,3 @@
+
+ custom-columns-dlg works!
+
diff --git a/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.scss b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.spec.ts b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.spec.ts
new file mode 100644
index 0000000..eb0661e
--- /dev/null
+++ b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { CustomColumnsDlgComponent } from './custom-columns-dlg.component';
+
+describe('CustomColumnsDlgComponent', () => {
+ let component: CustomColumnsDlgComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ CustomColumnsDlgComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(CustomColumnsDlgComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.ts b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.ts
new file mode 100644
index 0000000..43f0644
--- /dev/null
+++ b/APP/src/components/fancy-table/custom-columns-dlg/custom-columns-dlg.component.ts
@@ -0,0 +1,15 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'rbj-custom-columns-dlg',
+ templateUrl: './custom-columns-dlg.component.html',
+ styleUrls: ['./custom-columns-dlg.component.scss']
+})
+export class CustomColumnsDlgComponent implements OnInit {
+
+ constructor() { }
+
+ ngOnInit() {
+ }
+
+}
diff --git a/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.html b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.html
new file mode 100644
index 0000000..4f8fa50
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.html
@@ -0,0 +1,18 @@
+
\ No newline at end of file
diff --git a/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.scss b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.scss
new file mode 100644
index 0000000..1940c5c
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.scss
@@ -0,0 +1,91 @@
+.fancyGroupMenuContainer {
+ width: inherit;
+ position: sticky;
+ top: 0px;
+}
+.dx-tag-remove-button {
+ top: 0;
+ right: 10px;
+}
+::-webkit-scrollbar {
+ width: 0.3rem !important;
+}
+.fancyGroupMenu {
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ padding-left: 0;
+ margin-bottom: 0;
+ width: 220px;
+ max-height: 67vh;
+ overflow-y: auto;
+ padding-bottom: 1px;
+ &::before {
+ bottom: 0;
+ //background-color: rgba(0, 0, 0, 0.2);
+ //background-color: var(--palette-black-alpha-20, rgba(0, 0, 0, 0.2));
+ content: "";
+ left: 0;
+ position: absolute;
+ top: 0;
+ width: 3px;
+ height: inherit;
+ }
+
+ .menu-item {
+ display: block;
+ padding: 0.75rem 1.25rem;
+ margin-bottom: -1px;
+ position: relative;
+ cursor: pointer;
+ &:hover {
+ //background-color: rgba(3, 169, 244, 0.08);
+ background-color: rgba(240, 91, 65, 0.12);
+ &.altColor {
+ background-color: rgba(0, 0, 0, 0.04);
+ }
+ }
+ &.hasData {
+ color: rgba(240, 91, 65, 1);
+ &.altColor {
+ color: rgba(60, 186, 178, 1);
+ }
+ }
+ &.active {
+ border-radius: 2px;
+ //color: rgba(0, 90, 158, 1);
+ //color: var(--communication-foreground, rgba(0, 90, 158, 1));
+ //color: #03a9f4;
+ color: rgba(255, 255, 255, 1) !important;
+ background-color: rgba(240, 91, 65, 1);
+ font-weight: 600;
+ transition:
+ color 80ms cubic-bezier(0.165, 0.84, 0.44, 1),
+ background 80ms linear;
+ &::before {
+ //background-color: #0078d4;
+ //background-color: var(--communication-background, #0078d4);
+ content: "";
+ left: 0px;
+ top: 0px;
+ position: absolute;
+ width: 3px;
+ height: 100%;
+ }
+ &.altColor {
+ background-color: rgba(60, 186, 178, 1);
+ }
+ }
+ }
+ &.dropHover .menu-item:hover {
+ border-color: rgba(240, 91, 65, 1);
+ &.altColor {
+ border-color: rgba(60, 186, 178, 1);
+ }
+ border: nb-theme(border-basic-color-5);
+ border-style: dashed;
+ border-width: medium;
+ cursor: copy;
+ }
+}
diff --git a/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.spec.ts b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.spec.ts
new file mode 100644
index 0000000..cd4abbd
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { FancyGroupMenuComponent } from './fancy-group-menu.component';
+
+describe('FancyGroupMenuComponent', () => {
+ let component: FancyGroupMenuComponent;
+ let fixture: ComponentFixture>;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ declarations: [FancyGroupMenuComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(FancyGroupMenuComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.ts b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.ts
new file mode 100644
index 0000000..e401c32
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-group-menu/fancy-group-menu.component.ts
@@ -0,0 +1,95 @@
+import { ChangeDetectionStrategy, Component, inject, Injector, Input, NgZone, Output } from '@angular/core';
+import { FancyGroup } from '../fancy-row-column.model';
+import { Subject } from 'rxjs';
+import { FancySettings } from '../fancy-settings.model';
+import { DebounceTimer } from '../../../utilities/timer-utils';
+import { DropFileEvent } from '../../../shared/file-drag-drop/file-drag-drop.directive';
+
+@Component({
+ selector: 'rbj-fancy-group-menu',
+ templateUrl: './fancy-group-menu.component.html',
+ styleUrl: './fancy-group-menu.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class FancyGroupMenuComponent {
+ injector = inject(Injector);
+ ngZone = inject(NgZone);
+ @Input() usingAltColor = false;
+ deletingGroup = false;
+
+ private clearDroppingOnGroupKeyTimer = new DebounceTimer(100, () => {
+ this._droppingOnGroupKey = null;
+ this.droppingOnGroupKeyChange.next(null);
+ })
+ @Input() groups: FancyGroup[];
+
+ @Input() settings: FancySettings;
+ private _draggingRow: boolean;
+ public get draggingRow(): boolean {
+ return this._draggingRow;
+ }
+ @Input() public set draggingRow(v: boolean) {
+ if (this._draggingRow != v) {
+ this._draggingRow = v;
+ this.draggingRowChange.next(v);
+ }
+ }
+ @Output() draggingRowChange = new Subject()
+
+ @Output() itemDropInGroup = new Subject();
+
+ private _droppingOnGroupKey: string;
+ public get droppingOnGroupKey(): string {
+ return this._droppingOnGroupKey;
+ }
+ @Input() public set droppingOnGroupKey(v: string) {
+ if (this._droppingOnGroupKey != v) {
+ this._droppingOnGroupKey = v;
+ this.droppingOnGroupKeyChange.next(this.droppingOnGroupKey);
+ }
+ }
+ @Input() filteringGroupKey: string;
+ @Output() droppingOnGroupKeyChange = new Subject();
+ @Output() onFileDroppedInGroup = new Subject>();
+
+ @Output() clickOnGroup = new Subject>();
+ @Output() clickOnDeleteGroup = new Subject>();
+
+ constructor() {
+
+ }
+
+ public onGroupListMouseUp(group: FancyGroup, $event: MouseEvent) {
+ if (!this.deletingGroup) {
+
+ this.droppingOnGroupKey = group.groupKey;
+ if (this.draggingRow) {
+ this.itemDropInGroup.next(group.groupKey);
+ //this.draggingRow = false;
+ }
+ setTimeout(() => {
+ this.draggingRow = false;
+ this.droppingOnGroupKey = null;
+ }, 500);
+ }
+ }
+
+ fileDroppedInGroup(group: FancyGroup, event: DropFileEvent) {
+ if (group) {
+ group.uiFileDropList = event.files;
+ this.onFileDroppedInGroup.next(group);
+ }
+ }
+ clickGroup(group: FancyGroup) {
+ if (!this.deletingGroup) {
+ this.clickOnGroup.next(group);
+ }
+ }
+ removeGroup(group: FancyGroup) {
+ this.deletingGroup = true;
+ setTimeout(() => {
+ this.deletingGroup = false;
+ }, 50);
+ this.clickOnDeleteGroup.next(group);
+ }
+}
diff --git a/APP/src/components/fancy-table/fancy-row-column.model.ts b/APP/src/components/fancy-table/fancy-row-column.model.ts
new file mode 100644
index 0000000..dfd2095
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-row-column.model.ts
@@ -0,0 +1,185 @@
+import { SafeHtml } from "@angular/platform-browser";
+import { DropDownOption } from "../../entity/dropDownOption";
+import { TemplateRef } from "@angular/core";
+
+export enum FtEditableType {
+ NONE = 'none',
+ READONLY = 'readOnly',
+ EDITABLE = 'editable',
+}
+
+export enum FtTrigger {
+ NOOP = 'noop',
+ DOUBLE_CLICK = 'doubleClick',
+}
+export declare type FtTagType = 'info' | 'primary' | 'danger' | 'warning' | 'success' | 'secondary' | 'light' | 'dark';
+export declare type FtColType = 'text' | 'date' | 'dateTime' | 'dropdown' | 'number' | 'checkbox' | 'checkall' | 'currency' | 'tag' | 'template';
+export declare type FtFilterMode = 'startsWith' | 'contains' | 'allkeywords';
+export class FancyColumn {
+ public name: string;
+ public title: string;
+
+ /**
+ * Col data type, Defaults to text, available types:
+ * text, date, dateTime, dropdown, number, checkbox, checkall, currency, tag
+ */
+ public type?: FtColType = 'text'; // 'text', 'date', 'dropdown' etc. Defaults to 'text'
+ public dropdownSource?: DropDownOption[];
+ public class?: string; // apply this class to the cells
+ //public sort?: boolean;
+
+ /**
+ * Sort by this column, available types:'asc', 'desc'
+ */
+ public sortDirection?: string;
+ public filter?: any;
+
+ /**
+ * Col filter mode, Defaults to contains, available types:
+ * startsWith, contains, allkeyswords
+ */
+ public filterMode?: FtFilterMode = 'contains';
+ /**
+ * Col filter case sensitive setting, Defaults to `true`
+ */
+ public filterIgnoreCase?: boolean = true;
+ public filterPreparedValue?: boolean = false;
+ public dateRange?: any;
+ public group?: boolean;
+
+ /**
+ * Col visible setting, Defaults to `true`
+ */
+ public visible?: boolean = true;
+ /**
+ * Disable column hover tooltip, Defaults to `false`
+ */
+ public noToolTip?: boolean = false;
+ public editableType?: FtEditableType = FtEditableType.NONE;
+ public editingFieldIndex?: number;
+ public editingFieldWidth?: number;
+ public footerClass?: string; // apply this class to the cells in the footer
+ public valuePrepareFunction?: (value: T) => string | SafeHtml | number | TemplateRef;
+ /**
+ * `(optional)` Preparation function for tooltip, if not exist will using same value with col value
+ */
+ public toolTipPrepareFunction?: (value: T) => string;
+ public groupKeyPrepareFunction?: (value: T) => string;
+ public sortFunction?: (a: T, b: T) => number;
+ public filterFunction?: (value: FancyDisplayRow, input) => boolean;
+ public tagsPrepareFunction?: (value: T) => FancyTag[];
+
+ /**
+ * apply the width px to column 0 for auto width
+ */
+ public widthPx?: number;
+ /**
+ * apply the width rem to column 0 for auto width, will overwrite `widthPx`
+ */
+ public widthRem?: number;
+ /**
+ * apply the width % to column 0 for auto width, will overwrite `widthRem` and `widthPx`
+ */
+ public widthPct?: number;
+
+
+ /**
+ * maxWidth for text wrapping, calculated by `widthPct`, do NOT use this for now!
+ */
+ public maxWidth?: string;
+
+ public initFocusFilterInput?: boolean = false;
+
+ /**
+ * the tagging field name for `checkall` column type to track checked order
+ */
+ public taggingTarget?: string = null;
+
+ constructor(config: Partial) {
+ Object.assign(this, config);
+ }
+ /**
+ * When `allowDragAndDrop` is enabled, then this column is draggable, default as `true`
+ */
+ public draggable?: boolean = true;
+
+ /**
+ * Set `td colspan`, currently only works in footer
+ */
+ public colspan?: number = 1;
+
+ uiCheckAll?: boolean = false;
+ uiWidth?: string;
+}
+
+export class FancyTag {
+
+
+ /**
+ * status could be `info` , `primary` , `danger` , `warning` , `success`
+ */
+ constructor(text: string, status: FtTagType, actionTag: string = null, tooltip = null) {
+ this.text = text;
+ this.status = status;
+ this.tooltip = tooltip || text;
+ this.actionTag = actionTag;
+ }
+
+ text: string;
+ tooltip: string;
+ status: FtTagType;
+
+ actionTag: string;
+}
+
+export class FancyDisplayRow {
+
+ index: number;
+ seq: number;
+ /**
+ * Sequence number for UI only
+ */
+ uiSeq: number;
+ isGroupHeader: boolean;
+ groupKey?: any;
+ colHtmlTexts: any;
+ colTooltips: any;
+ rowDatum: T;
+ visible: boolean;
+ /**
+ * this is for custom groups to store row id for many to many relationships.
+ */
+ customRowId: string;
+
+ /**
+ * this only has value when file drop to the row
+ */
+ uiFileDropList: FileList | File[];
+}
+export class FancyGroup {
+ seq: number;
+ /**
+ * Origin value type of groupKey
+ */
+ groupKey: any;
+ /**
+ * Formatted string value of groupValue
+ */
+ groupValue: any;
+ rows: FancyDisplayRow[];
+ expanded: boolean;
+ /**
+ * this only has value when file drop to the Group
+ */
+ uiFileDropList: FileList | File[];
+ uiAllowDeleteWhenEmpty: boolean;
+}
+
+export class FancyColumnCustomSetting {
+ id: string
+ name: string
+ seq: number
+ visible: boolean
+ groupBy: boolean
+ widthPx: number
+}
\ No newline at end of file
diff --git a/APP/src/components/fancy-table/fancy-selection.model.ts b/APP/src/components/fancy-table/fancy-selection.model.ts
new file mode 100644
index 0000000..293f090
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-selection.model.ts
@@ -0,0 +1,22 @@
+import { FancyDisplayRow, FancyGroup } from "./fancy-row-column.model";
+
+export class FancySelection {
+ index: number;
+ focusedCustomRowId: string;
+ focusedRow: T;
+ selectedRows: T[];
+}
+
+export class FancyEditorInstance {
+ index: number;
+ object: T;
+}
+export class FancyDragDrop {
+ previousIndex: number;
+ currentIndex: number;
+ focusRow: FancyDisplayRow;
+ previousRow?: FancyDisplayRow;
+ nextRow?: FancyDisplayRow;
+ previousGroup?: FancyGroup;
+ currentGroup?: FancyGroup;
+}
\ No newline at end of file
diff --git a/APP/src/components/fancy-table/fancy-settings.model.ts b/APP/src/components/fancy-table/fancy-settings.model.ts
new file mode 100644
index 0000000..5ffb9a8
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-settings.model.ts
@@ -0,0 +1,188 @@
+import { Observable, Subject } from 'rxjs';
+import { FancyColumn, FancyGroup, FtTrigger } from './fancy-row-column.model';
+import { FancyEditorInstance, FancySelection } from './fancy-selection.model';
+import { EventEmitter } from '@angular/core';
+import { ContextMenuItem } from '../../directives/right-click-menu/context-menu-item.model';
+
+export const isFancyGroup = (o: T | FancyGroup): o is FancyGroup => {
+ return o && o.hasOwnProperty("groupKey") && typeof (o as FancyGroup).groupKey === "string";
+}
+export const isNotFancyGroup = (o: T | FancyGroup): o is T => {
+ return false == (o && o.hasOwnProperty("groupKey") && typeof (o as FancyGroup).groupKey === "string");
+}
+export class FancySettings {
+
+ /**
+ * Set width to 100%
+ */
+ public autoWidth?: boolean = true;
+ /**
+ * Displaying titles of each column, default as `true`
+ */
+ public showHeader?: boolean = true;
+ /**
+ * Displaying footer, default as `false`
+ */
+ public showFooter?: boolean = false;
+ /**
+ * Enable sticky footer, default as `false`
+ */
+ public stickyFooter?: boolean = false;
+ /**
+ * Displaying filter of each column, default as `true`
+ */
+ public showFilters?: boolean = true;
+ /**
+ * Displaying group side menu when group by a column, default as `false`
+ */
+ public showGroupsMenu?: boolean = false;
+
+ /**
+ * Allow user selecting row and hight light it, default as `true`
+ */
+ public allowSelection?: boolean = true;
+ /**
+ * Allow user drag and drop row, default as `false`
+ */
+ public allowDragAndDrop?: boolean = false;
+ /**
+ * Allow user drag and drop row, default as `false`
+ */
+ public allowDrag?: boolean = false;
+ /**
+ * When allow user drag and drop row, using drag icon instead of drag all row, default as `false`
+ */
+ public usingDragIcon?: boolean = false;
+ /**
+ * Allow user selecting multi rows, default as `false`
+ */
+ public multiselect?: boolean = false;
+ /**
+ * Allow user resizing columns, default as `true`
+ */
+ public resizingColumn?: boolean = true;
+
+ /**
+ * Allow user dropping files, default as `false`
+ */
+ public enableFileDrop?: boolean = false;
+
+ private _columns?: FancyColumn[];
+ public get columns(): FancyColumn[] {
+ return this._columns;
+ }
+ public set columns(v: FancyColumn[]) {
+ if (v) {
+ for (let i = 0; i < v.length; i++) {
+ v[i] = new FancyColumn({ ...v[i] });
+ }
+ }
+ this._columns = v;
+ }
+
+ // public groups: FancyGroup[];
+ public contextMenuItems?: ContextMenuItem[];
+ public onContextMenuOpening?: (datum: T | FancyGroup, rightClickMenuItems: ContextMenuItem[], selection: FancySelection, event: MouseEvent) => void;
+ public editorTrigger?: FtTrigger = FtTrigger.NOOP;
+
+ public groupMissing?: string = 'N/A';
+
+ /**
+ * Using custom groups object list
+ */
+ public customGroups?: any[] = null;
+ /**
+ * The group key field name of custom groups
+ */
+ public customGroupKeyFieldName?: string = 'id';
+ /**
+ * The group value field name of custom groups
+ */
+ public customGroupValueFieldName?: string = 'name';
+ /**
+ * The group seq field name of custom groups
+ */
+ public customGroupSeqFieldName?: string = 'seq';
+
+ /**
+ * The group rows field name of custom groups
+ */
+ public customGroupRowsFieldName?: string = '';
+
+ /**
+ * Group sort rule, available types:'asc', 'desc'
+ */
+ public groupSortDirection?: string = 'asc';
+ public groupSortFunction?: (a: FancyGroup, b: FancyGroup) => number;
+
+ /**
+ * For generating Group Row Description
+ */
+ public groupSeqPrepareFunction?: (groupKey: any) => number;
+ /**
+ * For generating Group Row Description
+ */
+ public groupValuePrepareFunction?: (groupKey: any, groupValues: T[]) => string;
+
+ /**
+ * Display group without data
+ */
+ public displayEmptyGroup?: boolean = false;
+ /**
+ * initial rows count for infinite scrolling, default as 50
+ */
+ public initialRows?: number = 50;
+ /**
+ * append rows count for infinite scrolling, default as 10
+ */
+ public appendRowsOnScroll?: number = 10;
+ /**
+ * Smooth scroll duration when using scrollToItem. Set to 0 to disable smooth scrolling.
+ */
+ public smoothScrollDurationMs?: number = 0;
+
+ /**
+ * adding validation for build-in editor, return `Observable` error message, if valid return `null`
+ */
+ public addingValidation?: (obj: FancyEditorInstance) => Observable | string;
+ /**
+ * editing validation for build-in editor, return `Observable` error message, if valid return `null`
+ */
+ public editingValidation?: (obj: FancyEditorInstance) => Observable | string;
+ /**
+ * deleting validation for build-in editor, return `Observable` error message, if valid return `null`
+ */
+ public deletingValidation?: (obj: FancyEditorInstance) => Observable | string;
+
+
+ /**
+ * the call back buttons for easy table picker
+ */
+ public headerCallbackButtons?: ContextMenuItem[];
+
+ /**
+ * Reload subject must be assigned from component setting
+ */
+ public reloadSubject?: Subject = null;
+
+ /**
+ * Reassign custom group and reload table,Reload subject must be assigned from component setting
+ */
+ public reloadSettingSubject?: Subject> = null;
+
+ /**
+ * The tagged row target field values, it's for `checkall` column type
+ */
+ public taggedValues?: any[] = [];
+ /**
+ * The tagged row custom row data, it's for `checkall` column type
+ */
+ public taggedRowData?: T[] = [];
+
+ public dialogCssClass?: string = null;
+
+
+ constructor(config: Partial>) {
+ Object.assign(this, config);
+ }
+}
diff --git a/APP/src/components/fancy-table/fancy-table.component.html b/APP/src/components/fancy-table/fancy-table.component.html
new file mode 100644
index 0000000..8fc9fb1
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-table.component.html
@@ -0,0 +1,239 @@
+
+
+ Clear All Filters
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{column.title}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ All
+ Disabled
+ Enabled
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{(settings.taggedValues.indexOf(row.rowDatum[column.taggingTarget])+1)}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No Match Found
+
+
+ Tip:
+ Please try another keyword.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/fancy-table/fancy-table.component.scss b/APP/src/components/fancy-table/fancy-table.component.scss
new file mode 100644
index 0000000..a00caf8
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-table.component.scss
@@ -0,0 +1,244 @@
+@import "../../@theme/styles/themes";
+
+@include nb-install-component() {
+ table.fancy {
+ table-layout: fixed;
+ border-collapse: separate !important;
+ border: 1px solid nb-theme(border-basic-color-5);
+ box-shadow: nb-theme(shadow);
+ border-top: 0px;
+ user-select: none;
+ border-spacing: 0;
+ table tbody,
+ table thead {
+ display: block;
+ }
+
+ th:first-child,
+ td:first-child {
+ /* Apply a left border on the first or in a row */
+ border-left: 1px solid nb-theme(border-basic-color-5);
+ }
+ td {
+ //display: block;
+ text-overflow: ellipsis;
+ //border: 1px solid nb-theme(border-basic-color-5);
+ padding: 5px 0.5rem; //0.5rem;
+ background: transparent;
+ border-bottom: 1px solid nb-theme(border-basic-color-5);
+ border-right: 1px solid nb-theme(border-basic-color-5);
+ }
+
+ tr.grouprow > td {
+ padding: 0.5rem;
+ }
+ tr.groupedItem > td {
+ padding: 0 0.5rem;
+ &.checkbox {
+ padding: 0.3rem 0.5rem;
+ }
+ }
+ td > div {
+ //width: 50px;
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ tr {
+ height: nb-theme(text-paragraph-font-size);
+ white-space: nowrap;
+ overflow: hidden;
+ }
+
+ tr.grouprow {
+ color: nb-theme(text-alternate-color);
+ background: nb-theme(color-primary-300) !important;
+ cursor: pointer;
+ user-select: none;
+ }
+
+ tr.collapsed {
+ display: none;
+ }
+
+ tbody {
+ tr:hover {
+ background: nb-theme(background-basic-color-4);
+ }
+
+ tr.selected {
+ color: nb-theme(text-alternate-color);
+ background: nb-theme(color-info-300);
+ }
+
+ tr.focused {
+ // border: 2px dotted nb-theme(color-info-900);
+ border: 2px dotted nb-theme(text-basic-color);
+
+ td {
+ border-top: 2px dotted nb-theme(text-basic-color);
+ border-bottom: 2px dotted nb-theme(text-basic-color);
+ }
+ td:first-child {
+ border-left: 2px dotted nb-theme(text-basic-color);
+ }
+ td:last-child {
+ border-right: 2px dotted nb-theme(text-basic-color);
+ }
+ }
+ }
+
+ tbody tr:nth-child(even):not(:hover):not(.selected) {
+ background: nb-theme(background-basic-color-1);
+ }
+
+ tbody tr:nth-child(odd):not(:hover):not(.selected) {
+ background: nb-theme(background-basic-color-2);
+ }
+ thead {
+ th {
+ background: white;
+ position: sticky;
+ top: 0;
+ //top: -16px;
+ box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.4);
+ height: 2.4rem;
+ padding: 0 5px;
+ background: nb-theme(background-basic-color-1);
+ border-bottom: 1px solid nb-theme(border-basic-color-5);
+ border-right: 1px solid nb-theme(border-basic-color-5);
+ z-index: 100;
+ }
+ tr:nth-child(1) th {
+ border-top: 2px solid nb-theme(border-basic-color-5);
+ cursor: pointer;
+ user-select: none;
+ }
+ tr:nth-child(2) th {
+ top: calc(2.4rem);
+ height: 3rem;
+ }
+ }
+ tfoot.stickyFooter {
+ td {
+ position: sticky;
+ bottom: 0;
+ box-shadow: 0 -2px 2px -1px rgba(0, 0, 0, 0.4);
+ height: 2.4rem;
+ padding: 0 5px;
+ background: nb-theme(background-basic-color-1);
+ border-top: 1px solid nb-theme(border-basic-color-5);
+ border-right: 1px solid nb-theme(border-basic-color-5);
+ z-index: 100;
+ }
+ tr:nth-child(1) td {
+ border-bottom: 2px solid nb-theme(border-basic-color-5);
+ }
+ }
+ }
+}
+table.fancy.overflow-x {
+ table-layout: auto;
+}
+// .searchclear {
+// position: absolute;
+// right: 5px;
+// top: 0;
+// height: 14px;
+// margin: auto;
+// font-size: 14px;
+// cursor: pointer;
+// }
+::ng-deep nb-checkbox.ftCheck {
+ width: 20px;
+ display: block;
+ > .label {
+ cursor: pointer;
+ }
+}
+.ftCheckBadge {
+ height: 17px;
+ margin-top: 3px;
+}
+.cdk-drag-preview {
+ box-sizing: border-box;
+ border-radius: 4px;
+ box-shadow:
+ 0 5px 5px -3px rgba(0, 0, 0, 0.2),
+ 0 8px 10px 1px rgba(0, 0, 0, 0.14),
+ 0 3px 14px 2px rgba(0, 0, 0, 0.12);
+}
+
+.cdk-drag-placeholder {
+ opacity: 0;
+}
+
+.cdk-drag-animating {
+ transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
+}
+.tableContainer {
+ height: 100%;
+ max-height: 100%; //calc(80vh - 20px);
+ overflow-y: auto;
+ overflow-x: hidden;
+ &.colResizing {
+ cursor: ew-resize !important;
+ }
+}
+.tableContainer.overflow-x {
+ overflow-x: auto;
+}
+
+:host {
+ //height: 100%;
+ //display: block;
+ //overflow: hidden;
+}
+.bottomShadow {
+ box-shadow: 0 2px 1rem 10px rgba(44, 51, 73, 0.25);
+ position: relative;
+ bottom: 4px;
+ width: 98%;
+ left: 30px;
+}
+
+.dragHandle {
+ position: absolute;
+ //background-color: cornflowerblue;
+ z-index: 2;
+ transform: translate(0, 0) !important;
+}
+
+.dragHandle.right {
+ right: 0px;
+ width: 2px;
+ height: 100%;
+ cursor: ew-resize;
+ top: 0;
+}
+
+.btnClearAllFilter {
+ /* Add animation */
+ -webkit-animation-name: fadeFromTop; /* Chrome, Safari, Opera */
+ -webkit-animation-duration: 0.5s; /* Chrome, Safari, Opera */
+ animation-name: fadeFromTop;
+ animation-duration: 0.5s;
+
+ position: absolute;
+ right: 28px;
+ margin-top: 41px;
+ z-index: 1011;
+ .clearTooltip {
+ -webkit-transition: width 0.5s ease-in-out;
+ -moz-transition: width 0.5s ease-in-out;
+ -o-transition: width 0.5s ease-in-out;
+ transition: width 0.5s ease-in-out;
+ width: 0;
+ overflow: hidden;
+ }
+ &:hover .clearTooltip {
+ padding-left: 6px;
+ width: 110px;
+ }
+}
diff --git a/APP/src/components/fancy-table/fancy-table.component.spec.ts b/APP/src/components/fancy-table/fancy-table.component.spec.ts
new file mode 100644
index 0000000..594a387
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-table.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { FancyTableComponent } from './fancy-table.component';
+
+describe('FancyTableComponent', () => {
+ let component: FancyTableComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ FancyTableComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(FancyTableComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/fancy-table.component.ts b/APP/src/components/fancy-table/fancy-table.component.ts
new file mode 100644
index 0000000..bef146e
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-table.component.ts
@@ -0,0 +1,1668 @@
+import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, ViewChild, ElementRef, OnInit, HostListener, AfterViewInit } from '@angular/core';
+import { FancySettings } from './fancy-settings.model';
+import { StringUtils } from '../../utilities/string-utils';
+import { LinqUtils } from '../../utilities/linq-utils';
+import { FancyDragDrop, FancyEditorInstance, FancySelection } from './fancy-selection.model';
+import { first, take, takeUntil } from 'rxjs/operators';
+import { UuidUtils } from '../../utilities/uuid-utils';
+import { MsgBoxService } from '../../services/msg-box.service';
+import { ADButtons, ADIcon } from '../alert-dlg/alert-dlg.model';
+import { DebounceTimer } from '../../utilities/timer-utils';
+import { CDK_DRAG_CONFIG, CdkDragDrop, CdkDragEnd, CdkDragMove, CdkDragStart, moveItemInArray, transferArrayItem } from '@angular/cdk/drag-drop';
+import { FancyDisplayRow, FancyGroup, FancyColumn, FancyTag, FtTrigger, FtEditableType, } from './fancy-row-column.model';
+import { Observable, of, Subject, Subscription } from 'rxjs';
+import { NumberUtils } from '../../utilities/number-utils';
+import { DateUtilsService } from '../../services/date-utils.service';
+import { ObjectUtils } from '../../utilities/object-utils';
+import { ArrayUtils } from '../../utilities/array-utils';
+import { DropFileEvent } from '../../shared/file-drag-drop/file-drag-drop.directive';
+import { Console } from 'console';
+import { CheckBoxComponent } from '@progress/kendo-angular-inputs';
+import { EasyEditorService } from '../../ui/easy-editor/easy-editor.service';
+import { ContextMenuItem } from '../../directives/right-click-menu/context-menu-item.model';
+
+const DATE_FORMAT = 'MM/dd/yyyy'
+const DATE_TIME_FORMAT = 'MM/dd/yyyy hh:mm aa'
+const HTML_INNERTEXT_REGEX = />((.|\n*?))<\//g;
+const SCROLLING_ROW_OFFSET = -150;
+const DragConfig = {
+ dragStartThreshold: 0,
+ pointerDirectionChangeThreshold: 5,
+ zIndex: 10000
+};
+
+@Component({
+ selector: 'fancy-table',
+ templateUrl: './fancy-table.component.html',
+ styleUrls: ['./fancy-table.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [{ provide: CDK_DRAG_CONFIG, useValue: DragConfig }]
+})
+export class FancyTableComponent implements OnInit, AfterViewInit {
+ // #region Properties (57)
+
+ private _afterReloadSubscription: Subscription;
+ private _data: T[];
+ private _draggingLeftColClientWidth: number;
+ private _draggingLeftColIndex: number;
+ private _footerData: T[];
+ private _footerSettings: FancySettings;
+ private _itemsPerPage: number = 10;
+ private _settings: FancySettings;
+ private _sortBySeq: boolean = false;
+ private destroy$: Subject = new Subject();
+
+ protected selectedRowIndex: number = undefined;
+
+ @Input() public id!: string;
+ @Input() public noDataMessage: string = "No Data Entered";
+ @Input() public usingAltColor: boolean = false;
+ @Output() public adding = new EventEmitter>();
+ @Output() public afterDisplayDataGenerated = new EventEmitter();
+ @Output() public afterReload = new EventEmitter();
+ @Output() public deleting = new EventEmitter>();
+ @Output() public doubleClick = new EventEmitter>();
+ @Output() public itemDrop = new EventEmitter();
+ @Output() public itemDropInGroup = new EventEmitter();
+ @Output() public onClickOnDeleteGroup = new EventEmitter>();
+ @Output() public onFileDropped = new EventEmitter>();
+ @Output() public onFileDroppedInGroup = new EventEmitter>();
+ @Output() public onFilterGroupKey = new EventEmitter>();
+ @Output() public onMouseUp = new EventEmitter>();
+ @Output() public onTagClick = new EventEmitter<{ actionTag: string, row: FancyDisplayRow }>();
+ @Output() public pressEnter = new EventEmitter>();
+ @Output() public rowSelected = new EventEmitter>();
+ @Output() public saving = new EventEmitter>();
+ @Output() public sortByColumn = new EventEmitter();
+ @ViewChild('checkAllBox', { static: false }) public checkAllBox: CheckBoxComponent;
+ @ViewChild('table', { static: true }) public tableToMeasureElement: ElementRef;
+
+ /**
+ * All FancyDisplayRow (Not Includes Group Header)
+ */
+ public allRows: FancyDisplayRow[];
+ public anyExpanded: boolean = true;
+ //private _draggingRightColClientWidth: number;
+ public colDragResizing: boolean = false;
+ public displayGroup: FancyGroup[];
+ public draggingRow = false;
+ public droppingOnGroupKey: string = null;
+ /**
+ * All generated displaying UI rows (Includes Group Header), the source of visibleData, if the row is visible,probably need to change the name of filteredRows
+ */
+ public filteredRows: FancyDisplayRow[];
+ public filteringDebounceTimer: DebounceTimer = new DebounceTimer(400, () => { this.generateVisibleData(); });
+ public filteringGroupKey: string = null;
+ public firstSelectedRow: FancyDisplayRow;
+ public footerDisplayData: FancyDisplayRow[];
+ public groupColumn: FancyColumn;
+ public groupValues = {};
+ public groups: FancyGroup[] = null;
+ public lastFocusedFilterInput: HTMLInputElement;
+ public maxPage: number;
+ public page: number = 1;
+ public pages: number[];
+ public processing: boolean = false;
+ //public resizingDebounceTimer: DebounceTimer = new DebounceTimer(500, () => { this.renderColMaxWidth() });
+ //output focusedRow and selectedRows
+ public selectedRows = new Set>();
+ public sortColumn: FancyColumn;
+ public tableId: string = UuidUtils.generate().substr(0, 9);
+ public visibleColumns: FancyColumn[];
+ /**
+ * Visible displaying UI rows (Includes Group Header), it's dynamic filteredRows base on scrolling
+ */
+ public visibleData: FancyDisplayRow[];
+
+ // #endregion Properties (57)
+
+ // #region Constructors (1)
+
+ constructor(
+ private dateUtilsService: DateUtilsService,
+ private el: ElementRef,
+ private cdRef: ChangeDetectorRef,
+ private easyEditorService: EasyEditorService,
+ private msgBoxService: MsgBoxService,
+ ) { }
+
+ // #endregion Constructors (1)
+
+ // #region Public Getters And Setters (14)
+
+ @Input() public set data(value: T[]) {
+ //
+ this._data = value;
+ if (this._data !== undefined) {
+ this.generateDisplayData();
+ //this._data.forEach((d, index) => d.index = index);
+ }
+ this.calculatePages(this._data);
+ }
+
+ @Input() public set footerData(value: any[]) {
+ this._footerData = value;
+ if (this.settings && value) {
+ this.generateFooterDisplayData();
+ }
+ }
+
+ // Optional. If not set, uses regular settings
+ @Input() public set footerSettings(value: FancySettings) {
+ this._footerSettings = value;
+ }
+
+ @Input() public set itemsPerPage(value: number) { // 0 to disable pager
+ this._itemsPerPage = Number(value);
+ this.calculatePages(this._data);
+ }
+
+ @Input() public set settings(value: FancySettings) {
+ this._settings = new FancySettings({ ...value });
+ if (value) {
+ this.initializeColumns();
+ if ((value.customGroups || this.data) && !this.visibleData) {
+ this.generateDisplayData();
+ if (this._settings.reloadSubject) {
+ this._settings.reloadSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
+ this.reload();
+ });
+ }
+ if (this._settings.reloadSettingSubject) {
+ this._settings.reloadSettingSubject.pipe(takeUntil(this.destroy$)).subscribe(result => {
+ this._settings = new FancySettings({ ...result });
+ this.reload();
+ });
+ }
+ }
+ }
+
+ // if (this.anyGroups) {
+ //
+ // this.grouped(this.filtered(this.data));
+ // }
+ }
+
+ public get anyFiltered(): boolean {
+ //null check for checkbox equals false
+ return this.settings.columns && this.settings.columns.some(c => {
+ if (c.filter != undefined && c.filter != '') return true;
+ if (['date', 'dateTime'].includes(c.type) && c.dateRange != undefined) return true;
+ return false;
+ }
+ );
+ }
+
+ public get anyGroups(): boolean {
+ return !!this._settings.customGroups || (this._settings.columns ? this._settings.columns.some(c => c.group) : false)
+ }
+
+ public get data(): T[] {
+ return this._data;
+ }
+
+ public get footerData(): any[] {
+ return this._footerData;
+ }
+
+ public get footerSettings(): FancySettings {
+ return this._footerSettings || this._settings;
+ }
+
+ public get hasScrollBar(): boolean {
+ var div = document.querySelector(`.tableContainer${this.tableId}`);
+ if (div) {
+ return div.scrollHeight > div.clientHeight;
+ }
+ return false;
+ }
+
+ public get itemsPerPage(): number {
+ return Number(this._itemsPerPage);
+ }
+
+ public get settings(): FancySettings {
+ return this._settings;
+ }
+
+ public get visibleColumnCount(): number {
+ if (this._settings) {
+ let count = this._settings.columns.map(d => d.visible ? 1 : 0).reduce((a, b) => a + b, 0);
+ if (this.anyGroups) {
+ count++;
+ }
+ return count;
+ }
+ return 0;
+ }
+
+ // #endregion Public Getters And Setters (14)
+
+ // #region Public Methods (68)
+
+ public ngOnInit(): void {
+ }
+
+ public ngAfterViewInit() {
+ // setTimeout(() => {
+ // this.renderColMaxWidth();
+ // }, 200);
+ console.log('Fancytable ngAfterViewInit');
+ }
+
+ public ngOnDestroy() {
+ this.destroy$.next();
+ this.destroy$.complete();
+ }
+
+ @HostListener('focus', ['$event'])
+ public focusLastAccessFilter() {
+ if (this.lastFocusedFilterInput) {
+ this.lastFocusedFilterInput.focus();
+ }
+ }
+
+ @HostListener('window:resize', ['$event'])
+ public screenResizing() {
+ //this.resizingDebounceTimer.resetTimer();
+ }
+
+ public addRow(defaultData: T = {}) {
+ this.openEditor(-1, defaultData);
+ // this.dialogService.open(FancyTableEditorComponent, {
+ // context: {
+ // columns: this.settings.columns,
+ // datum: defaultData,
+ // isAdding: true,
+ // index: -1,
+ // validation: this.settings.addingValidation
+ // },
+ // }).onClose.pipe(take(1)).subscribe(instance => {
+ // if (instance) {
+ // this.adding.emit({ index: -1, object: instance });
+ // }
+ // });
+ }
+
+ public checkboxCheckAll(col: FancyColumn, checked: boolean) {
+ //Ignore intermediate status
+ if (checked !== null) {
+ this.filteredRows.filter(r => r.visible && !r.isGroupHeader).forEach(datum => {
+ this.toggleCheckBox(col, datum, checked, true);
+ });
+ this.reload();
+ }
+ }
+
+ public checkboxGetFilter(filter) {
+ return {
+ undefined: 0,
+ true: 1,
+ false: 2,
+ }[filter];
+ }
+
+ public checkboxSetFilter(colName: string, filter: number) {
+ let checked = [
+ undefined,
+ true,
+ false,
+ ][filter];
+ this.settings.columns.find(c => c.name === colName).filter = checked;
+ this.generateVisibleData();
+ }
+
+ public clearAllFilters() {
+ for (let i = 0; i < this.settings.columns.length; i++) {
+ const col = this.settings.columns[i];
+ col.filter = undefined;
+ col.dateRange = undefined;
+ }
+ this.filteringGroupKey = null;
+ this.reload();
+ }
+
+ public colResizeDragResize(col: FancyColumn, $event: CdkDragMove) {
+ let leftColumn = this.visibleColumns[this._draggingLeftColIndex];
+ //let rightColumn = this.visibleColumns[this._draggingLeftColIndex + 1];
+ console.log('resize', $event.distance.x);
+
+ let offsetX = $event.distance.x;
+ leftColumn.widthPct = 0;
+ leftColumn.widthRem = 0;
+ leftColumn.widthPx = this._draggingLeftColClientWidth + offsetX;
+ this.calculateColWidth();
+ // rightColumn.widthPct = 0;
+ // rightColumn.widthRem = 0;
+ // rightColumn.widthPx = this._draggingRightColClientWidth - offsetX;
+ }
+
+ public colResizeDragStart(col: FancyColumn, $event: CdkDragStart) {
+ this.colDragResizing = true;
+
+ let colIndex = this.visibleColumns.indexOf(col);
+ this._draggingLeftColIndex = colIndex;// < (this.visibleColumns.length - 1) ? colIndex : (colIndex - 1);
+
+ this._draggingLeftColClientWidth = document.getElementById(`${this.tableId}col-${this._draggingLeftColIndex}`).clientWidth;
+ //this._draggingRightColClientWidth = document.getElementById(`${this.tableId}col-${this._draggingLeftColIndex + 1}`).clientWidth;
+ }
+
+ public colResizeEnd(col: FancyColumn, $event: CdkDragEnd) {
+ this.colDragResizing = false;
+ }
+
+ public columnFilterFunction(column: FancyColumn): boolean {
+ return (column.filter !== undefined || column.filterFunction !== undefined || column.dateRange !== undefined);
+ }
+
+ public deleteRow(selectedRowIndex: number = undefined) {
+ if (undefined === selectedRowIndex) {
+ selectedRowIndex = this.firstSelectedRow.index;//this.data.findIndex(d => this.selectedRows.has(d));
+ }
+ let _deletingIndex = selectedRowIndex;
+ let deletingInstance = { index: _deletingIndex, object: this.data[_deletingIndex] };
+
+ let msg = null;
+ let async = false;
+ if (this.settings.deletingValidation) {
+ let tempResult = this.settings.deletingValidation(deletingInstance);
+ if (typeof tempResult === 'string') {
+ msg = tempResult;
+ }
+ else {
+ async = true;
+ tempResult.pipe(first()).subscribe(result => {
+ this.showDeletingConfirm(result, deletingInstance);
+ });
+ }
+ }
+ if (false == async) {
+ this.showDeletingConfirm(msg, deletingInstance);
+ }
+ }
+
+ public detectChanges() {
+ if (!this.cdRef['destroyed']) {
+ this.cdRef.detectChanges();
+ }
+ }
+
+ public drop(event: CdkDragDrop) {
+ console.log('dropRow', this.droppingOnGroupKey);
+ this.draggingRow = false;
+ if (this.droppingOnGroupKey) {
+ return;
+ }
+ if (false == this.settings.allowDragAndDrop || event.currentIndex == event.previousIndex) return;
+
+ //let datum = this.visibleData.find(d => d.uiSeq == event.previousIndex);
+ //if (false == this.selectedRows.has(datum)) this.selectedRows.add(datum);
+
+ //moveItemInArray(this.visibleData, event.previousIndex, event.currentIndex);
+ let previousGroup: FancyGroup = null;
+ let currentGroup: FancyGroup = null;
+
+ let previousRow = undefined as FancyDisplayRow;
+ let nextRow = undefined as FancyDisplayRow;
+ let moveDown = event.currentIndex > event.previousIndex;
+ //if it's not moving to the end of table, should always has next row at current index before resorting
+ if (event.currentIndex != (this.visibleData.length - 1)) {
+ const displayRow = this.visibleData[event.currentIndex + (moveDown ? 1 : 0)];
+ if (!displayRow.isGroupHeader) {
+ nextRow = displayRow;
+ console.log('nextRow', displayRow.rowDatum['description']);
+ }
+ }
+
+ //if current index > 0 should always has previous row at current index-1
+ if (event.currentIndex > 0) {
+ const displayRow = this.visibleData[event.currentIndex - (moveDown ? 0 : 1)];
+ if (!displayRow.isGroupHeader) {
+ previousRow = displayRow;
+ console.log('previousRow', displayRow.rowDatum['description']);
+ }
+ }
+
+ var offset = 0;
+ let selectedRows = Array.from(this.selectedRows)
+
+ if (this.settings.taggedRowData)
+
+ if (this.anyGroups) {
+ let moveDown = event.previousIndex < event.currentIndex;
+ currentGroup = this.groups.slice().reverse().find(g => g.seq < (event.currentIndex + (moveDown ? 1 : 0)));
+ }
+
+ for (let i = 0; i < selectedRows.length; i++) {
+ const row = selectedRows[i];
+ let moveTo = i == 0 ? event.currentIndex : (this.filteredRows.indexOf(selectedRows[0]) + i - 1);
+
+ if (this.anyGroups) {
+ previousGroup = this.groups.slice().reverse().find(g => g.seq < row.uiSeq);
+ }
+
+ moveItemInArray(this.filteredRows, this.filteredRows.indexOf(row), moveTo);
+ if (this.anyGroups && previousGroup && currentGroup && previousGroup != currentGroup) {
+ //expands group if it collapsed
+ if (false == currentGroup.expanded) currentGroup.expanded = true;
+ row.groupKey = currentGroup.groupKey;
+ if (this.groupColumn) {
+ row.rowDatum[this.groupColumn.name] = currentGroup.groupValue;
+ }
+
+ let insertIndex = 0;
+ if (currentGroup.rows.length > 0) {
+ insertIndex = previousRow ? Math.max(0, currentGroup.rows.indexOf(previousRow)) : currentGroup.rows.length;
+ }
+
+ transferArrayItem(previousGroup.rows, currentGroup.rows, previousGroup.rows.indexOf(row), insertIndex)
+ }
+ }
+ this.filteredRows.forEach((row, i) => {
+ row.uiSeq = i;
+ row.seq = i;
+ });
+ //Enable sort by Seq after drag
+ this._sortBySeq = true;
+ this.generateVisibleData(false);
+ //Set focus to drag and drop row
+ this.firstSelectedRow = this.filteredRows[event.currentIndex];
+ this.selectedRows = new Set([this.firstSelectedRow]);
+ //this.setFocusedRowByUiSeq(event.currentIndex);
+ this.itemDrop.emit({
+ previousIndex: event.previousIndex, currentIndex: event.currentIndex,
+ previousGroup, currentGroup, previousRow, nextRow,
+ focusRow: this.firstSelectedRow,
+ });
+ }
+
+ public dropColumn(event: CdkDragDrop) {
+ }
+
+ public expandedGroups(key: any): boolean {
+ let group = this.getGroupByKey(key);
+ return group && group.expanded;
+ }
+
+ public fileDropped(row: FancyDisplayRow, event: DropFileEvent) {
+ if (row == null) {
+ row = new FancyDisplayRow();
+ }
+ row.uiFileDropList = event.files;
+ this.onFileDropped.next(row);
+
+ console.log('FancyTable file drop', row);
+ //{{this.tableId}}-GroupRow-{{row.index}}
+ //{{this.tableId}}-Row-{{row.index}}
+ //this.onFileDropped.next(files);
+ }
+
+ public filterGroupKey(group: FancyGroup) {
+ if (!this.draggingRow) {
+ if (this.filteringGroupKey == group.groupKey) {
+ this.filteringGroupKey = null;
+ } else {
+ this.filteringGroupKey = group.groupKey;
+ }
+ this.visibleData = [];
+ this.generateVisibleData(true);
+ this.onFilterGroupKey.emit(group);
+ }
+ }
+
+ public filtered(data: FancyDisplayRow[]): FancyDisplayRow[] {
+ // Todo: configure this somewhere in FancySettings
+ let filterAll = true;
+
+ if (data) {
+ data.forEach(datum => {
+ datum.visible = false;
+ });
+
+ data.filter(d => {
+ let filteredColumns = this.settings.columns.filter(this.columnFilterFunction);
+ if (filterAll) {
+ return filteredColumns.every(c => this.rowFilterFunction(d, c));
+ }
+ else { // filter Any
+ return filteredColumns.some(c => this.rowFilterFunction(d, c)); // unused until filterAll is configurable
+ }
+ }).forEach(d => d.visible = true);
+ }
+ return data;
+ }
+
+ public generateDisplayData() {
+ this.filteredRows = [];
+ this.allRows = [];
+ if (this.settings && (this.data || this.settings.customGroups)) {
+ //Do NOT clean up visible data here, that will cause table reload and lose dynamic data
+ //this.visibleData = [];
+ if (this.settings.customGroups) {
+ //this.groups = null;
+ } else {
+ this.data.forEach((d, index) => {
+ //d.index = index;
+ this.allRows.push({
+ seq: index,
+ uiSeq: index,
+ index: index,
+ rowDatum: d,
+ colHtmlTexts: {},//ObjectUtils.Clone(d),
+ colTooltips: {},//ObjectUtils.Clone(d),
+ isGroupHeader: false
+ } as FancyDisplayRow);
+ });
+
+ this.visibleColumns.forEach(col => {
+ if (!col.group && col.visible) {
+ this.allRows.forEach(datum => {
+ this.getFieldDisplayValue(datum, col);
+ //datum[col.name] = col.valuePrepareFunction(datum);
+ });
+ col.uiWidth = this.getColWidthSetting(col);
+ }
+ });
+ }
+
+ this.generateVisibleData();
+ this.generateFooterDisplayData();
+ this.afterDisplayDataGenerated.emit();
+ }
+ }
+
+ public generateFooterDisplayData() {
+ this.footerDisplayData = [];
+ if (this.settings && this.footerData) {
+ this.footerData.forEach((d, index) => {
+ this.footerDisplayData.push({
+ seq: index,
+ index: index,
+ rowDatum: d,
+ colHtmlTexts: {},
+ colTooltips: {},
+ isGroupHeader: false
+ } as FancyDisplayRow);
+ });
+
+ this.footerSettings.columns.filter(f => f.visible).forEach(col => {
+ if (!col.group) {
+ this.footerDisplayData.forEach(datum => {
+ this.getFieldDisplayValue(datum, col);
+ //datum[col.name] = col.valuePrepareFunction(datum);
+ });
+ }
+ });
+ this.detectChanges();
+ }
+ }
+
+ public generateVisibleData(reGrouping: boolean = true) {
+ let visibleRowCount = this.visibleData && this.visibleData.length > this.settings.initialRows ? this.visibleData.length : this.settings.initialRows;
+
+ if (!this.settings.customGroups) {
+ this.filtered(this.allRows);
+ }
+
+ //with grouping
+ if (this.anyGroups /*&& !this.groups || Object.keys(this.groups).length === 0*/) {
+ this.filteredRows = [];
+ let lastExpandSetting = this.groups ? this.groups.map(
+ g => { return { groupKey: g.groupKey, expanded: g.expanded } }
+ ) : [];
+ if (!this.groups || reGrouping) {
+ if (this.settings.customGroups) {
+ this.groups = [];
+ this.allRows = [];
+ for (let i = 0; i < this.settings.customGroups.length; i++) {
+ const customGroup = this.settings.customGroups[i];
+ let groupKey = customGroup[this.settings.customGroupKeyFieldName];
+ const rows = customGroup[this.settings.customGroupRowsFieldName].map((d, index) => {
+ return {
+ seq: index,
+ index: index,
+ rowDatum: d,
+ groupKey: groupKey,
+ colHtmlTexts: {},
+ colTooltips: {},
+ isGroupHeader: false
+ } as FancyDisplayRow
+ }
+ ) as FancyDisplayRow[];
+ this.allRows = this.allRows.concat(rows);
+
+ this.visibleColumns.forEach(col => {
+ if (!col.group && col.visible) {
+ rows.forEach(datum => {
+ this.getFieldDisplayValue(datum, col);
+ //datum[col.name] = col.valuePrepareFunction(datum);
+ });
+ }
+ });
+ let groupDisplayValue = customGroup[this.settings.customGroupValueFieldName];
+ this.groups.push({
+ seq: customGroup[this.settings.customGroupSeqFieldName],
+ groupKey: groupKey,
+ groupValue: this.settings.groupValuePrepareFunction ? this.settings.groupValuePrepareFunction(groupDisplayValue, rows) : groupDisplayValue,
+ rows: this.filtered(rows),
+ expanded: true,
+ uiAllowDeleteWhenEmpty: customGroup['uiAllowDeleteWhenEmpty']
+ } as FancyGroup);
+ }
+ } else {
+ this.groups = this.grouped(this.allRows.filter(d => d.visible));
+ }
+
+ if (this.settings.groupSortDirection) {
+ this.groups = this.sortedGroupKeys(this.groups);
+ }
+
+ if (this.settings.customGroups) {
+ //this.allRows = ArrayUtils.ConcatMultiArray(this.groups.map(g => g.rows));
+ for (let i = 0; i < this.allRows.length; i++) {
+ const row = this.allRows[i];
+ row.index = i;
+ row.uiSeq = i;
+ }
+ }
+ }
+
+ //Adding Group header
+ this.groups.forEach(group => {
+ if (!this.filteringGroupKey || this.filteringGroupKey == group.groupKey) {
+ if (group.rows.filter(r => r.visible).length > 0 || this.settings.displayEmptyGroup) {
+ this.filteredRows.push({ isGroupHeader: true, groupKey: group.groupKey } as FancyDisplayRow);
+ group.expanded = lastExpandSetting.length == 0 || lastExpandSetting.find(s => s.groupKey == group.groupKey)?.expanded;
+ if (group.expanded === undefined) {
+ group.expanded = true;
+ }
+ //Sort grouped data
+ if (group.expanded) {
+ this.filteredRows = this.filteredRows.concat(this.sorted(group.rows));
+ }
+ }
+ }
+ });
+ }
+ else {
+ //Sorting
+ this.filteredRows = this.sorted(this.allRows.filter(d => d.visible));
+ }
+
+ this.visibleData = this.filteredRows.filter(r => r.isGroupHeader || r.visible).slice(0, visibleRowCount);
+ this.allRows.forEach(row => row.uiSeq = -1);
+ this.filteredRows.forEach((row, i) => {
+ row.uiSeq = i;
+ if (row.isGroupHeader) this.getGroupByKey(row.groupKey).seq = i;
+ });
+ this.detectChanges();
+ }
+
+ public getColWidthSetting(col: FancyColumn) {
+ if (col.type == 'checkall') {
+ return !col.taggingTarget ? '37px' : '55px';
+ } else if (col.widthPct) {
+ return col.widthPct.toString() + '%';
+ } else if (col.widthRem) {
+ return col.widthRem.toString() + 'rem';
+ } else if (col.widthPx) {
+ return col.widthPx.toString() + 'px';
+ }
+ return 'auto';
+ }
+
+ public getFieldDisplayValue(row: FancyDisplayRow, column: FancyColumn) {
+ if (column.valuePrepareFunction) {
+ let preparedValue = column.valuePrepareFunction(row.rowDatum);
+ row.colHtmlTexts[column.name] = preparedValue == null ? '' : preparedValue;
+ } else {
+ switch (column.type) {
+ case "tag":
+ let tags = column.tagsPrepareFunction(row.rowDatum);
+ row.colHtmlTexts[column.name] = tags;
+ row.colTooltips[column.name] = StringUtils.makeCommaSeparatedString(tags.map(t => t.tooltip), true);
+ break;
+ case "checkall":
+ //need to set all row to false by default to avoid intermediate status of the check box
+ if ([undefined, null].includes(row.rowDatum[column.name])) {
+ row.rowDatum[column.name] = false;
+ };
+ break;
+ default:
+ row.colHtmlTexts[column.name] = row.rowDatum[column.name];
+ break;
+ }
+ }
+
+ switch (column.type) {
+ case "currency":
+ row.colHtmlTexts[column.name] = NumberUtils.FormatCurrency(row.colHtmlTexts[column.name], "0.00");
+ break;
+
+ case "date":
+ row.colHtmlTexts[column.name] = this.dateUtilsService.format(row.rowDatum[column.name], DATE_FORMAT);
+ break;
+
+ case "dateTime":
+ row.colHtmlTexts[column.name] = this.dateUtilsService.format(row.rowDatum[column.name], DATE_TIME_FORMAT);
+ break;
+ }
+
+ if (column.toolTipPrepareFunction) {
+ row.colTooltips[column.name] = column.toolTipPrepareFunction(row.rowDatum);
+ } else if (!row.colTooltips[column.name]) {
+ //Parse HTML inner text for tooltip, can comment this out if got performance problem
+ row.colTooltips[column.name] = StringUtils.getHtmlInnerText('' + row.colHtmlTexts[column.name]);
+ }
+
+ return row.colHtmlTexts[column.name];
+ }
+
+ public getGroupByKey(groupKey) {
+ return this.groups.find(g => g.groupKey == groupKey);
+ }
+
+ public getGroupValue(key: any) {
+ return this.getGroupByKey(key).groupValue;
+ }
+
+ public getScrollHeightOfIndex(index: number = -1): number {
+ return 100;
+ }
+
+ public getSelected() {
+ return >{
+ index: this.firstSelectedRow && this.firstSelectedRow.index >= 0 ? this.firstSelectedRow.index : -1,
+ focusedRow: this.firstSelectedRow && this.firstSelectedRow.rowDatum || undefined,
+ focusedCustomRowId: this.firstSelectedRow && this.firstSelectedRow.customRowId || undefined,
+ selectedRows: Array.from(this.selectedRows.keys()).map(r => r?.rowDatum) as T[],
+ }
+ }
+
+ public gotoPage(p: number) {
+ this.page = p;
+ }
+
+ public grouped(data: FancyDisplayRow[]): FancyGroup[] {
+ // only update grouping the first time, TODO: or on a change to group settings
+ if (this.groupColumn /*&& !this.groups || Object.keys(this.groups).length === 0*/) {
+ data.forEach(row => {
+ row.groupKey = this.groupColumn.groupKeyPrepareFunction ? this.groupColumn.groupKeyPrepareFunction(row.rowDatum) : row.rowDatum[this.groupColumn.name];
+
+ });
+
+ let groups = ArrayUtils.GroupBy(data, r => r.groupKey);
+ let tempList: FancyGroup[] = [];
+ var groupCount = 0;
+ for (let i = 0; i < groups.length; i++) {
+ const item = groups[i];
+ let group = {
+ groupKey: item.key,
+ seq: this.settings.groupSeqPrepareFunction ? this.settings.groupSeqPrepareFunction(item.key) : item.values[0].seq + groupCount,
+ rows: item.values,
+ expanded: true
+ } as FancyGroup;
+ group.groupValue = this.settings.groupValuePrepareFunction ? this.settings.groupValuePrepareFunction(item.key, group.rows.map(r => r.rowDatum)) : item.key;
+ if (!group.groupValue) {
+ //TODO:add configuration
+ group.groupValue = this.settings.groupMissing;
+ }
+ group.rows = this.sorted(group.rows);
+ tempList.push(group);
+ groupCount++;
+ }
+ this.groups = tempList;
+ }
+
+ return this.groups;
+ }
+
+ public keyHandler(event: KeyboardEvent) {
+ switch (event.key) {
+ case 'ArrowDown':
+ {
+ let currentUiSeq = this.firstSelectedRow ? this.firstSelectedRow.uiSeq : -1;
+ currentUiSeq = NumberUtils.Clamp(currentUiSeq, -1, this.visibleData.length - 2);
+ this.setFocusedRowByUiSeq(currentUiSeq + 1);
+ }
+ event.preventDefault();
+ break;
+
+ case 'ArrowUp':
+ {
+ let currentUiSeq = this.firstSelectedRow ? this.firstSelectedRow.uiSeq : -1;
+ currentUiSeq = NumberUtils.Clamp(currentUiSeq, 1, this.visibleData.length);
+ this.setFocusedRowByUiSeq(currentUiSeq - 1);
+ }
+ event.preventDefault();
+ break;
+ case 'Enter':
+ if (this.pressEnter.observers.length > 0) {
+ this.pressEnter.next(this.getSelected());
+ event.preventDefault();
+ }
+ break;
+ }
+ }
+
+ public markForCheck() {
+ this.cdRef.markForCheck();
+ }
+
+ public onCellMouseDown(col: FancyColumn, $event: MouseEvent) {
+ if (this.settings.allowDragAndDrop && !col.draggable) {
+ $event.stopPropagation();
+ }
+ }
+
+ // has this [RightClickValue]="this", so "self" is "this" FancyTableComponent
+ // "this" is not in the click MouseEvent scope, afaik
+ public onContextMenuOpening(self: FancyTableComponent, rightClickMenuItems: ContextMenuItem[], event: MouseEvent) {
+ // datum will be undefined, since context menu is bound to tbody and unset
+
+ let targetElement = event.target as Element;
+
+ let targetRow = self.getTrFromChild(targetElement);
+
+ if (self.groupColumn) {
+ let targetGroup = self.getGroupKeyFromTR(targetRow) as FancyGroup;
+ if (targetGroup) {
+ rightClickMenuItems.forEach(function (item, index) {
+ item.visible = item.forGroupHeader == true;
+ });
+ if (self.settings.onContextMenuOpening) {
+ self.settings.onContextMenuOpening(targetGroup, rightClickMenuItems, self.getSelected(), event);
+ }
+ return targetGroup;
+ } else {
+ }
+ }
+
+ let targetDatum = self.getDatumFromTR(targetRow);
+
+ rightClickMenuItems.forEach(function (item, index) {
+ item.visible = item.forGroupHeader == false;
+ });
+ let rowDatum = targetDatum ? targetDatum.rowDatum : null;
+ self.onRowSelect(targetDatum ? targetDatum.index : -1, event);
+ if (self.settings.onContextMenuOpening) {
+ self.settings.onContextMenuOpening(rowDatum, rightClickMenuItems, self.getSelected(), event);
+ }
+ return rowDatum;
+ }
+
+ public onInfinityScrollDown() {
+ this.visibleData = this.visibleData.concat(this.filteredRows.slice(this.visibleData.length, this.visibleData.length + this.settings.appendRowsOnScroll));
+ }
+
+ public onInfinityScrollUp() {
+ }
+
+ // getGroupDisplayValue(group) {
+ // let fancyGroup = this.settings && this.settings.groups.find(g => g.name === group.key);
+ // if (fancyGroup && fancyGroup.valuePrepareFunction)
+ // return fancyGroup.valuePrepareFunction(group.key);
+ // return group.key;
+ // }
+ public onRowMouseUp(row: FancyDisplayRow, $event: MouseEvent) {
+ this.onMouseUp.emit(row);
+ }
+
+ public onRowSelect(index: number, event: MouseEvent) {
+ if (!this.settings.allowSelection) return;
+ //let targetElement = event.srcElement as Element;
+ let targetRow = this.allRows[index];
+ //let datum = targetRow.rowDatum;
+
+ if (this.selectedRows.has(targetRow) && !(event.ctrlKey || event.shiftKey)) {
+ //when user click same row without control or shift key, don't need to clean out selectedRows
+ return;
+ }
+
+ //
+
+ if (!this.settings.multiselect || !(event.ctrlKey || event.shiftKey)) {
+ //if (false == (this.settings.taggedValues && this.settings.taggedValues.length > 1)) {
+ //don't reset if the using the check box for row selection
+ this.selectedRows = new Set();
+ this.firstSelectedRow = undefined;
+ //}
+ }
+
+ if (this.firstSelectedRow === undefined) {
+ this.firstSelectedRow = targetRow;
+ // console.log("firstSelectedRow", datum)
+ } else if (this.selectedRows.has(targetRow) && this.firstSelectedRow === targetRow && this.selectedRows.size === 1) {
+ this.firstSelectedRow = undefined;
+ }
+
+ // select all from `datum` to `firstSelectedRow`
+ if (event.shiftKey) {
+ let data = this.visibleData;
+ let datumFound = false;
+ let firstSelectedRowFound = false;
+
+ for (let i = 0; i < data.length; i++) {
+ if (false == data[i].isGroupHeader) {
+ if (data[i] === targetRow) {
+ datumFound = true;
+ }
+ if (data[i] === this.firstSelectedRow) {
+ firstSelectedRowFound = true;
+ }
+
+ if (datumFound != firstSelectedRowFound || data[i] === targetRow || data[i] === this.firstSelectedRow) { // xor
+ this.selectedRows.add(data[i]);
+ } else if (this.selectedRows.has(data[i])) {
+ this.selectedRows.delete(data[i]);
+ }
+ }
+ }
+ } else {
+ this.selectedRows.has(targetRow) ? this.selectedRows.delete(targetRow) : this.selectedRows.add(targetRow);
+ }
+
+ this.rowSelected.emit(this.getSelected());
+ }
+
+ /**
+ * If editingIndex is not supplied or undefined, use the current selected row.
+ * Set editingIndex to -1 to enable Create mode.
+ */
+ public openEditor(editingIndex: number = undefined, defaultData: T = {}) {
+ if (undefined === editingIndex) {
+ // selectedRowIndex = this.data.findIndex(d => this.selectedRows.has(d));
+ editingIndex = this.firstSelectedRow.index;
+ }
+
+ const datum = editingIndex > -1 ? this.data[editingIndex] : defaultData; // or {}?
+
+ let onEditorClosed = this.easyEditorService.openEditorForFancyTable(datum, this.settings.columns, editingIndex);
+
+ onEditorClosed.pipe(first()).subscribe(result => {
+ if (result && result.object) {
+ const instance = result.object;
+ if (editingIndex > -1) {
+ if (!this.saving.observed) {
+ // allow for default behavior if not subscribed to saving event
+ this.data[editingIndex] = instance;
+ this.reload();
+ } else {
+ this.saving.emit({ index: editingIndex, object: instance });
+ }
+ } else {
+ if (!this.adding.observed) {
+ // allow for default behavior if not subscribed to adding event
+ this.data.push(instance);
+ this.reload();
+ }
+ else {
+ this.adding.emit({ index: editingIndex, object: instance });
+ }
+ }
+ }
+ });
+
+ return onEditorClosed;
+ }
+
+ public reload() {
+ this.initializeColumns();
+ this.generateDisplayData();
+ // this.groups = undefined;
+ this.detectChanges();
+ this.afterReload.emit();
+ }
+
+ public renderColMaxWidth(): boolean {
+ let divToMeasureWidth = this.el.nativeElement.clientWidth;
+ if (divToMeasureWidth > 0) {
+ this._settings.columns.forEach(col => {
+ let maxWidth = 'auto';
+ if (col.visible) {
+ if (col.widthPct) {
+ maxWidth = `${divToMeasureWidth * col.widthPct / 100}px`;
+ } else if (col.widthRem) {
+ maxWidth = col.widthRem.toString() + 'rem';
+ } else if (col.widthPx) {
+ maxWidth = col.widthPx.toString() + 'px';
+ }
+ }
+ col.maxWidth = maxWidth;
+ });
+ this.detectChanges();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Will return UI seq if found it other wise return `-1`,
+ * Example:`fancyTable.renderRowHtml(s => s.id == 'The id')`
+ */
+ public renderRowHtml(predicate: (value: T) => boolean, focusAfterRender: boolean = false): number {
+ let index = -1;
+ const searchResult = this.visibleData.filter(d => d.rowDatum && predicate(d.rowDatum));
+ if (searchResult) {
+ for (let i = 0; i < searchResult.length; i++) {
+ const row = searchResult[i];
+
+ this.visibleColumns.forEach(col => {
+ if (!col.group && col.visible) {
+ this.getFieldDisplayValue(row, col);
+ }
+ });
+ if (index == -1) {
+ index = row.index;
+ }
+ }
+ }
+ if (focusAfterRender) {
+ this.setFocusedRowByIndex(index);
+ }
+ this.detectChanges();
+ return index;
+ }
+
+ public resetSort() {
+ this.sortColumn = null;
+ if (this.settings && this.settings.columns) {
+ this.sortColumn = this.settings.columns.find(c => !StringUtils.isNullOrWhitespace(c.sortDirection));
+ }
+ }
+
+ public rowFilterFunction(row: FancyDisplayRow, c: FancyColumn): boolean {
+ if (row.isGroupHeader) return true;
+ // Use filterFunction if it was defined
+ if (c.filterFunction) {
+ return c.filterFunction(row, c.filter);
+ }
+
+ // Use filter string if it was defined
+ if ((c.filter !== undefined || c.dateRange !== undefined) /*&& d[c.name]*/) {
+ switch (c.type || 'text') {
+ case 'date':
+ case 'dateTime':
+
+ if (c.dateRange) {
+ // If a filter is set and the date is null, filter it out
+ if (false == this.dateUtilsService.isValidDate(row.rowDatum[c.name])) {
+ return false;
+ }
+
+ let start = this.dateUtilsService.getLocalISOTime(c.dateRange.start);
+ let end = this.dateUtilsService.getLocalISOTime(this.dateUtilsService.addDays(c.dateRange.end, 1)); // increment day by 1
+ if (!c.dateRange.end) {
+ c.dateRange.end = this.dateUtilsService.getLocalISOTime(this.dateUtilsService.addDays(c.dateRange.start, 1));
+ }
+
+ if (!start) start = this.dateUtilsService.getLocalISOTime(this.dateUtilsService.MIN);
+ if (!end) end = this.dateUtilsService.getLocalISOTime(this.dateUtilsService.MAX);
+
+ // let value = typeof d[c.name] === 'Date' ? d[c.name] : new Date(d[c.name]);
+ let value = this.dateUtilsService.getLocalISOTime(new Date(row.rowDatum[c.name]));
+ //
+
+ //
+ return start <= value && value < end;
+ } else {
+ return true;
+ }
+
+ case 'dropdown':
+ case 'text':
+ case 'number':
+ case 'currency':
+ {
+ let strval = ((c.valuePrepareFunction && c.filterPreparedValue) || c.type == 'currency') ? row.colHtmlTexts[c.name] : '' + row.rowDatum[c.name];
+ strval = strval.toString();
+ let value = c.filterIgnoreCase ? strval.toUpperCase() : strval;
+ let filter = c.filter ? (c.filterIgnoreCase ? ('' + c.filter).toUpperCase() : '' + c.filter) : '';
+
+ switch (c.filterMode) {
+ case 'startsWith':
+ return value.startsWith(filter);
+ case 'allkeywords':
+ return !filter || (filter && filter.split(' ').every(token => value.includes(token)));
+ case 'contains':
+ default:
+ return value.includes(filter);
+ }
+ }
+
+ case 'checkbox':
+ {
+ let value = row.rowDatum[c.name];
+ let filter = c.filter;
+ //
+ //
+ return value === filter;
+ }
+ case 'tag':
+ {
+ let filter = c.filter ? (c.filterIgnoreCase ? c.filter.toUpperCase() : c.filter) : '';
+ if (filter) {
+ if (c.filterPreparedValue) {
+ let strval = row.rowDatum[c.name] || '';
+ let value = c.filterIgnoreCase ? strval.toUpperCase() : strval;
+ return value.includes(filter);
+ } else {
+ let tags: FancyTag[] = row.colHtmlTexts[c.name];
+ return tags.some(tag => (tag.text ? (c.filterIgnoreCase ? tag.text.toUpperCase() : tag.text) : '').indexOf(filter) > -1);
+ }
+ }
+ }
+ // case 'text':
+ // if (false === StringUtils.isNullOrWhitespace(d[c.name])) {
+ // let value = d[c.name].toUpperCase();
+ // let filter = c.filter.toUpperCase();
+ // switch (c.filterMode) {
+ // case 'contains':
+ // return value.includes(filter);
+ // default:
+ // return value.startsWith(filter);
+ // }
+ // }
+ }
+ }
+
+ // If no filters, return true
+ return true;
+ }
+
+ public scrollTo(y: number) {
+ if (document.querySelector('.tableContainer' + this.tableId)) {
+ document.querySelector('.tableContainer' + this.tableId).scrollTop = y;
+ }
+ }
+
+ public scrollToItem(itemIndex: number) {
+ let currentVisibleRowsContainsItem = this.visibleData.some(d => d.index == itemIndex);
+ if (currentVisibleRowsContainsItem) {
+ let targetRow = this.allRows.find(r => r.index == itemIndex);
+ //TODO:Optimize this, the problem here is the visibleData do contains the item but the uiSeq and itemIndex is not match.
+ if (targetRow && !this.visibleData.some(d => d.uiSeq == targetRow.uiSeq)) {
+ //check if the item already append to visible data
+ while (this.visibleData.length < targetRow.uiSeq) {
+ this.onInfinityScrollDown();
+ }
+ }
+ }
+
+ const el = document.getElementById(`${this.tableId}${itemIndex}`);
+ if (el) {
+ const tableContainer = document.querySelector('.tableContainer' + this.tableId);
+ const finalScroll = el['offsetTop'] + SCROLLING_ROW_OFFSET;
+ const initialScroll = tableContainer.scrollTop;
+ const deltaScroll = finalScroll - initialScroll;
+
+ if (this.settings.smoothScrollDurationMs > 0) {
+ const duration = this.settings.smoothScrollDurationMs;
+ const step = duration / 60; // 60 fps
+ let current = 0;
+ const scrollInterval = setInterval(() => {
+ current += step;
+ let scrollAmount = deltaScroll * this.smoothstep(0, duration, current);
+ tableContainer.scrollTop = initialScroll + scrollAmount;
+ if (current >= duration) {
+ clearInterval(scrollInterval);
+ }
+ }, step);
+ } else {
+ tableContainer.scrollTop = finalScroll;
+ }
+ //el.scrollIntoView({ behavior: 'smooth' })
+ }
+ }
+
+ private smoothstep(edge0: number, edge1: number, x: number) {
+ // Scale, and clamp x to 0..1 range
+ x = this.clamp((x - edge0) / (edge1 - edge0));
+
+ return x * x * (3.0 - 2.0 * x);
+ }
+
+ private clamp(x: number, lowerlimit: number = 0.0, upperlimit: number = 1.0) {
+ if (x < lowerlimit) return lowerlimit;
+ if (x > upperlimit) return upperlimit;
+ return x;
+ }
+
+ /**
+ * Will return found rows count,
+ * Example:`fancyTable.searchMultiRowsAndSelect(s => idArray.some(id=>id===s.id))`
+ */
+ public searchMultiRowsAndSelect(predicate: (value: T) => boolean): number {
+ this.selectedRows = new Set();
+ let count = 0;
+ const searchResult = this.filteredRows.filter(d =>
+ d.rowDatum && predicate(d.rowDatum));
+ if (searchResult) {
+ searchResult.forEach(row => {
+ this.selectedRows.add(row);
+ });
+ count = searchResult.length;
+ if (count > 0) {
+ this.scrollToItem(this.allRows.indexOf(searchResult[0]));
+ }
+ }
+ this.detectChanges();
+
+ return count;
+ }
+
+ /**
+ * Will return UI seq if found it other wise return `-1`,
+ * Example:`fancyTable.searchRowAndFocus(s => s.id == 'The id')`
+ */
+ public searchRowAndFocus(predicate: (value: T) => boolean, focusAfterDisplayDataRegen: boolean = false): number {
+ if (focusAfterDisplayDataRegen) {
+ this.afterDisplayDataGenerated.pipe(first()).subscribe(result => {
+ this.searchRowAndFocus(predicate, false);
+ this.detectChanges();
+ });
+ //this.reload();
+ } else {
+ let index = -1;
+ const searchResult = this.filteredRows.find(d => d.rowDatum && predicate(d.rowDatum));
+ if (searchResult) {
+ index = searchResult.index;
+ }
+ this.setFocusedRowByIndex(index);
+ this.detectChanges();
+ return index;
+ }
+ return 0;
+ }
+
+ public setColEditableType(name: string, type: FtEditableType, fieldWidth = -1) {
+ let col = this.settings.columns.find(c => c.name == name);
+ if (col) {
+ col.editableType = type;
+ if (fieldWidth > -1) {
+ col.editingFieldWidth = fieldWidth;
+ }
+ }
+ }
+
+ public setFocusedFilterInput(event: FocusEvent) {
+ this.lastFocusedFilterInput = event ? event.target : null;
+ }
+
+ public setFocusedRowByIndex(index: number) {
+ if (index === -1) {
+ this.selectedRows = new Set();
+ this.firstSelectedRow = undefined;
+ this.rowSelected.emit(this.getSelected());
+ }
+ else if (this.filteredRows.length >= index + 1) {
+ this.firstSelectedRow = this.filteredRows.find(r => r.index == index);
+ this.selectedRows = new Set([this.firstSelectedRow]);
+ this.rowSelected.emit(this.getSelected());
+
+ //Need a UI change detection here but doesn't need to regenerate display data, doesn't need to call this.reload();
+ this.detectChanges();
+
+ this.scrollToItem(index);
+ }
+ }
+
+ public setFocusedRowByUiSeq(uiSeq: number) {
+ if (uiSeq === -1) {
+ this.selectedRows = new Set();
+ this.firstSelectedRow = undefined;
+ this.rowSelected.emit(this.getSelected());
+ }
+ else {
+ let focusRow = this.filteredRows.find(r => r.uiSeq == uiSeq);
+ if (focusRow) {
+ this.firstSelectedRow = focusRow;
+ this.selectedRows = new Set([focusRow]);
+ this.rowSelected.emit(this.getSelected());
+
+ //Need a UI change detection here but doesn't need to regenerate display data, doesn't need to call this.reload();
+ this.detectChanges();
+
+ this.scrollToItem(focusRow.index);
+ }
+ }
+ }
+
+ public setSort(column: FancyColumn) {
+ let sortDirection = 'asc';
+ if (this.sortColumn &&
+ this.sortColumn.name == column.name &&
+ this.sortColumn.sortDirection === sortDirection) {
+ sortDirection = 'desc';
+ }
+
+ this.sortColumn = { ...column } as FancyColumn;
+ this.sortColumn.sortDirection = sortDirection;
+
+ this.generateVisibleData();
+ this.detectChanges();
+ this.sortByColumn.next(this.filteredRows.filter(d => d.rowDatum).map(d => d.rowDatum));
+ }
+
+ public showHideGroup(groupKey) {
+ let group = this.getGroupByKey(groupKey);
+
+ group.expanded = !group.expanded;
+ //this.anyExpanded = Object.keys(this.expandedGroups).some(k => this.expandedGroups[k]);
+ this.generateVisibleData(false);
+ if (group.expanded && group.rows.length > 1) {
+ this.onInfinityScrollDown();
+ this.detectChanges();
+ setTimeout(() => {
+ this.scrollToItem(group.rows[0].index);
+ }, 200);
+ }
+ }
+
+ // public selectAll() {
+ // if (0 < this.data.length) {
+ // delete this.selectedRows; // useful?
+ // this.firstSelectedRow = this.data[0];
+ // this.selectedRows = new Set(this.data);
+ // this.reload();
+ // }
+ // }
+ public showProcessing() {
+ this.processing = true;
+ setTimeout(() => {
+ this.processing = false;
+ }, 300);
+ }
+
+ public slice(data: T[], start: number, length: number) {
+ if (this.itemsPerPage > 0 && data) {
+ return data.slice(start, start + length);
+ }
+ return data;
+ }
+
+ public sorted(data: FancyDisplayRow[]): FancyDisplayRow[] {
+ if (data && data.length > 0) {
+ if (this._sortBySeq) {
+ return data.sort((a, b) => a.uiSeq - b.uiSeq);
+ }
+ else if (this.sortColumn) {
+ // Todo: these values should be enum'd, or a function lookup table or something
+ let descendingSort = this.sortColumn.sortDirection === 'desc';
+ let doStringSort = (this.sortColumn.valuePrepareFunction && this.sortColumn.filterPreparedValue) || typeof (data[0].rowDatum[this.sortColumn.name]) === 'string';
+
+ let doDateSort = this.sortColumn.type == 'date' || this.sortColumn.type == 'dateTime';
+ let doTagSort = false;
+ if (this.sortColumn.type == 'tag') {
+ doTagSort = true;
+ doStringSort = true;
+ }
+
+ // const sortFunctions = {typeof date.getMonth === 'function'
+ // 'string': (a, b) => a.localeCompare(b),
+ // ... etc
+ // }
+
+ const sortedDataFunction = (a: FancyDisplayRow, b: FancyDisplayRow) => {
+ if (this.sortColumn.sortFunction) {
+ return this.sortColumn.sortFunction(a.rowDatum, b.rowDatum);
+ }
+
+ let aval = a.rowDatum[this.sortColumn.name];
+ let bval = b.rowDatum[this.sortColumn.name];
+
+ if (doTagSort) {
+ aval = a.colTooltips[this.sortColumn.name];
+ bval = b.colTooltips[this.sortColumn.name];
+
+ } else if (this.sortColumn.valuePrepareFunction && this.sortColumn.filterPreparedValue) {
+ aval = a.colHtmlTexts[this.sortColumn.name];
+ bval = b.colHtmlTexts[this.sortColumn.name];
+ }
+
+ if (doDateSort) {
+ if (doStringSort) {
+ aval = this.dateUtilsService.parse(aval);
+ bval = this.dateUtilsService.parse(bval);
+ }
+ return this.dateUtilsService.getTime(aval) - this.dateUtilsService.getTime(bval);
+ } else if (doStringSort) {
+ return StringUtils.compare(aval, bval);
+ } else {
+ return ((!aval && aval !== 0) || (!bval && bval !== 0)) ? 0 : aval - bval;
+ }
+ };
+
+ if (descendingSort) {
+ return data.sort((a, b) => sortedDataFunction(b, a)); // swapped a and b
+ }
+ else {
+ return data.sort((a, b) => sortedDataFunction(a, b));
+ }
+ }
+ }
+ return data;
+ }
+
+ public sortedGroupKeys(groups: FancyGroup[]) {
+ //
+ if (groups) {
+ let descendingSort = this.settings.groupSortDirection === 'desc';// swapped a and b
+
+ const sortFunction = (a: FancyGroup, b: FancyGroup) => {
+ if (this.settings.groupSortFunction) {
+ return descendingSort ? this.settings.groupSortFunction(b, a) : this.settings.groupSortFunction(a, b);
+ }
+ if (this.settings.groupSeqPrepareFunction) {
+ return descendingSort ? (b.seq - a.seq) : (a.seq - b.seq);
+ }
+ switch (typeof (a.groupKey)) {
+ case 'string':
+ return descendingSort ? b.groupKey.localeCompare(a.groupKey) : a.groupKey.localeCompare(b.groupKey);
+ default:
+ return descendingSort ? (b.groupKey - a.groupKey) : (a.groupKey - b.groupKey);
+ }
+ };
+
+ return groups.sort(sortFunction);
+ }
+ else {
+ return [];
+ }
+ }
+
+ public startDragging() {
+ this.draggingRow = true;
+ }
+
+ public tagOnClick(row: FancyDisplayRow, actionTag: string) {
+ if (actionTag) {
+ this.onTagClick.next({ row, actionTag });
+ }
+ }
+
+ public toFancyColumn(v): FancyColumn {
+ return v as FancyColumn;
+ }
+
+ public toggleCheckBox(taggingColumn: FancyColumn, row: FancyDisplayRow, $event: boolean, bulkUpdate = false) {
+ let datum = row.rowDatum;
+ datum[taggingColumn.name] = $event;
+ if (taggingColumn.taggingTarget) {
+ let rowId = datum[taggingColumn.taggingTarget];
+ if ($event) {
+ if (!this.settings.taggedValues.includes(rowId)) {
+ this.settings.taggedValues.push(rowId);
+ this.settings.taggedRowData.push(datum);
+ }
+
+ //TODO:The checkbox adding to selected rows still too buggy, circle back to this latter
+ // if (!this.selectedRows.has(row)) {
+ // this.selectedRows.add(row);
+ // }
+ // this.firstSelectedRow = row;
+
+ } else {
+ let removeIndex = this.settings.taggedValues.indexOf(rowId);
+ if (removeIndex > -1) {
+ this.settings.taggedValues.splice(removeIndex, 1);
+ this.settings.taggedRowData.splice(removeIndex, 1);
+ }
+
+ // if (this.selectedRows.has(row)) {
+ // this.selectedRows.delete(row);
+ // }
+
+ // if (this.selectedRows.size > 0) {
+ // this.firstSelectedRow = this.selectedRows[this.selectedRows.size - 1];
+ // } else {
+ // this.firstSelectedRow = null;
+ // }
+ }
+
+ if (!bulkUpdate) {
+ this.rowSelected.emit(this.getSelected());
+ }
+ }
+ if (!bulkUpdate) {
+ let dataRows = this.filteredRows.filter(r => !r.isGroupHeader);
+ let checkedRowCount = dataRows.filter(r => r.rowDatum[taggingColumn.name]).length;
+ taggingColumn.uiCheckAll = checkedRowCount == dataRows.length ? true : checkedRowCount == 0 ? false : null;
+ //this.checkAllBox.ngDoCheck();
+ // if ($event) {
+ // this.setFocusedRowByIndex(row.index);
+ // }
+ }
+ // if (!bulkUpdate && taggingColumn.taggingTarget && this.settings.taggedValues && this.settings.taggedValues.length > 0) {
+ // taggingColumn.uiCheckAll = null;
+ // this.checkAllBox.ngDoCheck();
+ // }
+ //taggingColumn.uiCheckAll= taggingColumn.taggingTarget&& this.settings.taggedValues&& this.settings.taggedValues.length>0
+ }
+
+ public toggleExpandAll() {
+ let expanded = !this.anyExpanded;
+ this.groups.forEach(group => {
+ group.expanded = expanded;
+ });
+ this.anyExpanded = expanded;
+ this.generateVisibleData(false);
+ }
+
+ public trackByUiSeq(index: number, item: FancyDisplayRow) {
+ return item.uiSeq;
+ }
+
+ public triggerDoubleClick(row: FancyDisplayRow) {
+ let selectedRows = this.getSelected();
+ if (selectedRows.index === -1) {
+ // if selection is disabled this should still return the row that was double clicked
+ selectedRows = >{
+ index: row?.index ?? -1,
+ focusedRow: row?.rowDatum,
+ focusedCustomRowId: row?.customRowId,
+ selectedRows: row ? [row.rowDatum] : [],
+ };
+ }
+ this.doubleClick.emit(selectedRows);
+ if (this.settings.editorTrigger == FtTrigger.DOUBLE_CLICK) {
+ if (row && row.index > -1) {
+ this.openEditor(row.index);
+ }
+ }
+ }
+
+ // #endregion Public Methods (68)
+
+ // #region Private Methods (7)
+
+ private calculateColWidth() {
+ this.visibleColumns.forEach(col => {
+ if (!col.group && col.visible) {
+ col.uiWidth = this.getColWidthSetting(col);
+ }
+ });
+ }
+
+ private calculatePages(data: T[]) {
+ if (this.itemsPerPage > 0 && data && data.length > 0) {
+ this.maxPage = Math.ceil(data.length / this.itemsPerPage);
+ this.pages = Array.from({ length: this.maxPage }, (x, i) => i + 1);
+ }
+ else {
+ this.maxPage = 1;
+ this.pages = [1,];
+ this.page = 1;
+ }
+ }
+
+ private getDatumFromTR(tr: Element): FancyDisplayRow {
+ if (tr) {
+ //
+ let rowId = tr.id.substr(this.tableId.length);
+ //
+
+ let rowIndex = Number(rowId);
+ //
+
+ return this.allRows[rowIndex];
+ }
+ else {
+ return null;
+ }
+ }
+
+ private getGroupKeyFromTR(tr: Element): FancyGroup {
+ if (tr && tr.attributes['groupkey']) {
+ return this.groups.find(g => g.groupKey == tr.attributes['groupkey'].value);
+ }
+ else {
+ return null;
+ }
+ }
+
+ private getTrFromChild(element: Element): Element {
+ if (element && element.id && element.id.startsWith(this.tableId)) {
+ // console.log(element.id)
+ return element;
+ }
+ else if (element && element.parentElement) {
+ return this.getTrFromChild(element.parentElement);
+ }
+ else {
+ return null;
+ }
+ }
+
+ private initializeColumns() {
+ if (this.settings && this.settings.columns) {
+ this.sortColumn = this.settings.columns.find(c => !StringUtils.isNullOrWhitespace(c.sortDirection));
+ this.visibleColumns = this.settings.columns.filter(c => c.visible && !c.group);
+ this.groupColumn = this.settings.columns.find(c => c.group);
+
+ this.calculateColWidth();
+ }
+ }
+
+ private showDeletingConfirm(validationMsg: string, instance: FancyEditorInstance) {
+ if (null === validationMsg) {
+ this.msgBoxService.showConfirmDeleteBox().pipe(first()).subscribe(answer => {
+ if (answer === true) {
+ if (this.deleting.observed) {
+ this.deleting.emit(instance);
+ } else {
+ // allow for default behavior if not subscribed to deleting event
+ this.data.splice(instance.index, 1);
+ this.reload();
+ }
+ }
+ });
+ }
+ else {
+ this.msgBoxService.show('Invalid Operation',
+ { text: validationMsg, icon: ADIcon.WARNING, buttons: ADButtons.OK });
+ }
+ }
+
+ // #endregion Private Methods (7)
+}
diff --git a/APP/src/components/fancy-table/fancy-table.module.ts b/APP/src/components/fancy-table/fancy-table.module.ts
new file mode 100644
index 0000000..47c1253
--- /dev/null
+++ b/APP/src/components/fancy-table/fancy-table.module.ts
@@ -0,0 +1,75 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FancyTableComponent } from './fancy-table.component';
+import { FormsModule } from '@angular/forms';
+import {
+ NbButtonModule,
+ NbDatepickerModule,
+ NbCalendarRangeModule,
+ NbCardModule,
+ NbCheckboxModule,
+ NbDialogModule,
+ NbIconModule,
+ NbInputModule,
+ NbSelectModule,
+ NbTooltipModule,
+ NbAlertModule,
+ NbSpinnerModule,
+ NbActionsModule,
+} from '@nebular/theme';
+import { PagerModule } from '../pager/pager.module';
+import { CurrencyInputModule } from '../currency-input/currency-input.module';
+import { DropDownListModule } from '../drop-down-list/drop-down-list.module';
+import { DateInputModule } from '../date-input/date-input.module';
+import { ConfirmDialogComponent } from './confirm-dialog/confirm-dialog.component';
+import { DragDropModule } from '@angular/cdk/drag-drop';
+import { InfiniteScrollModule } from 'ngx-infinite-scroll';
+import { TablePickerComponent } from './table-picker/table-picker.component';
+import { CustomColumnsDlgComponent } from './custom-columns-dlg/custom-columns-dlg.component';
+import { FileDragDropModule } from '../../shared/file-drag-drop/file-drag-drop.module';
+import { InputsModule } from '@progress/kendo-angular-inputs';
+import { FancyGroupMenuComponent } from './fancy-group-menu/fancy-group-menu.component';
+import { EmailInputModule } from '../email-input/email-input.module';
+import { PhoneInputModule } from '../phone-input/phone-input.module';
+import { RightClickMenuModule } from '../../directives/right-click-menu/right-click-menu.module';
+import { InitFocusModule } from '../../directives/init-focus/init-focus.module';
+
+const components = [
+ FancyTableComponent,
+ ConfirmDialogComponent,
+]
+
+@NgModule({
+ declarations: [...components, TablePickerComponent, CustomColumnsDlgComponent, FancyGroupMenuComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbButtonModule,
+ NbCalendarRangeModule,
+ NbCardModule,
+ NbCheckboxModule,
+ NbDatepickerModule,
+ NbDialogModule,
+ NbIconModule,
+ NbInputModule,
+ NbSelectModule,
+ NbTooltipModule,
+ NbAlertModule,
+ NbSpinnerModule,
+ NbActionsModule,
+ InfiniteScrollModule,
+ CurrencyInputModule,
+ DateInputModule,
+ DragDropModule,
+ DropDownListModule,
+ PagerModule,
+ RightClickMenuModule,
+ InitFocusModule,
+ FileDragDropModule,
+ PhoneInputModule,
+ EmailInputModule,
+ InputsModule
+ ],
+ exports: [...components]
+})
+export class FancyTableModule { }
diff --git a/APP/src/components/fancy-table/seq-calc.service.spec.ts b/APP/src/components/fancy-table/seq-calc.service.spec.ts
new file mode 100644
index 0000000..5673904
--- /dev/null
+++ b/APP/src/components/fancy-table/seq-calc.service.spec.ts
@@ -0,0 +1,12 @@
+import { TestBed } from '@angular/core/testing';
+
+import { SeqCalcService } from './seq-calc.service';
+
+describe('SeqCalcService', () => {
+ beforeEach(() => TestBed.configureTestingModule({}));
+
+ it('should be created', () => {
+ const service: SeqCalcService = TestBed.get(SeqCalcService);
+ expect(service).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/seq-calc.service.ts b/APP/src/components/fancy-table/seq-calc.service.ts
new file mode 100644
index 0000000..8e62bcc
--- /dev/null
+++ b/APP/src/components/fancy-table/seq-calc.service.ts
@@ -0,0 +1,162 @@
+import { Inject, Injectable, Optional } from '@angular/core';
+import { FancyDragDrop } from './fancy-selection.model';
+import { ISeqBusinessEntity } from '../../models/base.model';
+import { moveItemInArray } from '@angular/cdk/drag-drop';
+import { UpdateSeqViewModel } from '../../shared/models/common.model';
+import { NumberUtils } from '../../utilities/number-utils';
+
+export const SEQ_INTERVAL = 'SEQ_INTERVAL_DEFAULT';
+export const DEFAULT_SEQ_INTERVAL = 1000;
+@Injectable({
+ providedIn: 'root'
+})
+export class SeqCalcService {
+ private SEQ_INTERVAL_REGULAR_AMOUNT = 10; // Regular interval amount for seq calculation
+ private baseInterval: number;
+ constructor(
+ @Optional() @Inject(SEQ_INTERVAL) baseInterval: number | null,
+ ) {
+
+ // use DEFAULT when token missing
+ this.baseInterval = baseInterval ?? DEFAULT_SEQ_INTERVAL;
+
+ }
+
+ calculateAndAssignSeq(event: FancyDragDrop,
+ allData: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void) {
+ let movedRows = [event.focusRow.rowDatum] as ISeqBusinessEntity[];
+ this.calculateAndAssignSeqs(event, allData, movedRows, updateSeqFunc);
+ }
+ calculateAndAssignSeqs(event: FancyDragDrop,
+ allData: ISeqBusinessEntity[],
+ movedRows: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void) {
+ let intervalCount = this.baseInterval;
+ let insertSeq = 0;
+ //allData = allData.sort((a, b) => a.seq - b.seq); // Ensure allData is sorted by seq before processing
+ //Find the start seq, and interval amount for moving
+ if (event.previousRow && event.nextRow) {
+ console.log('previousRowSeq', event.previousRow.rowDatum.seq, event.nextRow.rowDatum.seq);
+ if (movedRows.length > 1) {
+ insertSeq = (event.previousRow.rowDatum.seq + event.nextRow.rowDatum.seq) / (movedRows.length + 1);
+ intervalCount = insertSeq + (insertSeq % 2);
+ } else {
+ insertSeq = (event.previousRow.rowDatum.seq + event.nextRow.rowDatum.seq) / 2;
+ }
+ } else if (event.previousRow) {
+ insertSeq = event.previousRow.rowDatum.seq + this.baseInterval;
+ }
+ else if (event.nextRow) {
+ intervalCount = event.nextRow.rowDatum.seq / movedRows.length;
+ intervalCount += intervalCount % 2;
+ }
+ let updateDocuments = [] as ISeqBusinessEntity[];
+ // if (this.tableSettings.taggedRowData.length > 0) {
+ // movedRows = this.tableSettings.taggedRowData as DocumentGroupTemplate[];
+ // }
+
+ //moveItemInArray(allData, event.previousIndex, event.currentIndex);
+ let needToUpdateAllRows = false;
+ let lastSeq = null;
+ for (let i = 0; i < movedRows.length; i++) {
+ const row = movedRows[i];
+ row.seq = Math.round(insertSeq + intervalCount * i);
+ if (lastSeq != null && (row.seq - lastSeq) === 0) {
+ needToUpdateAllRows = true;
+ break;
+ }
+ lastSeq = row.seq;
+ allData.find(d => d.id == row.id).seq = row.seq;
+ if (allData.filter(d => d.seq == row.seq).length > 1) {
+ needToUpdateAllRows = true;
+ }
+ updateDocuments.push(row);
+ console.log('newSeq', row.seq);
+ }
+
+ allData = allData.sort((a, b) => a.seq - b.seq); // Ensure allData is sorted by seq after processing
+
+ if (needToUpdateAllRows) {
+ this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
+ } else {
+
+ updateSeqFunc(
+ { ids: updateDocuments.map(d => d.id), seqs: updateDocuments.map(d => d.seq) } as UpdateSeqViewModel
+ );
+ }
+
+ }
+
+ resetAllSeq(allData: ISeqBusinessEntity[], updateSeqFunc: (model: UpdateSeqViewModel) => void, startSeq: number = 0) {
+ let seq = startSeq;
+ for (let i = 0; i < allData.length; i++) {
+ const document = allData[i];
+ document.seq = seq;
+ seq += this.baseInterval;
+ }
+ if (updateSeqFunc) {
+ updateSeqFunc(
+ { ids: allData.map(d => d.id), seqs: allData.map(d => d.seq) } as UpdateSeqViewModel
+ );
+ }
+ }
+
+ calculateNewSeq(
+ allData: ISeqBusinessEntity[],
+ focusedDatum: ISeqBusinessEntity,
+ insertAfter: boolean,
+ updateSeqFunc: (model: UpdateSeqViewModel) => void
+ ): number {
+ if (!Array.isArray(allData) || !focusedDatum) {
+ throw new Error("Invalid arguments.");
+ }
+
+ const base = this.baseInterval;
+ const idx = allData.indexOf(focusedDatum);
+ if (idx === -1) throw new Error("focusedDatum is not in allData.");
+
+
+ // Helper: resequence using the current array order
+ const resequence = () => {
+ for (let i = 0; i < allData.length; i++) {
+ allData[i].seq = (i + 1) * base;
+ }
+ };
+
+ let newSeq = 0;
+
+ if (insertAfter) {
+ // Append at end
+ if (idx === allData.length - 1) {
+ newSeq = focusedDatum.seq + base;
+ } else {
+ const next = allData[idx + 1];
+ const gap = next.seq - focusedDatum.seq;
+ if (gap >= 2) {
+ newSeq = NumberUtils.Mid(focusedDatum.seq, next.seq);
+ } else {
+ // No integer room; normalize then take the middle
+ //resequence();
+ this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
+ newSeq = NumberUtils.Mid(allData[idx].seq, allData[idx + 1].seq);
+ }
+ }
+ } else {
+ // Insert before
+ if (idx === 0) {
+ newSeq = focusedDatum.seq - base;
+ } else {
+ const prev = allData[idx - 1];
+ const gap = focusedDatum.seq - prev.seq;
+ if (gap >= 2) {
+ newSeq = NumberUtils.Mid(prev.seq, focusedDatum.seq);
+ } else {
+ //resequence();
+ this.resetAllSeq(allData, updateSeqFunc, allData[0].seq);
+ newSeq = NumberUtils.Mid(allData[idx - 1].seq, allData[idx].seq);
+ }
+ }
+ }
+
+ return newSeq;
+ }
+}
diff --git a/APP/src/components/fancy-table/table-picker/table-picker.component.html b/APP/src/components/fancy-table/table-picker/table-picker.component.html
new file mode 100644
index 0000000..49d122b
--- /dev/null
+++ b/APP/src/components/fancy-table/table-picker/table-picker.component.html
@@ -0,0 +1,22 @@
+
+
+ {{title}}
+
+
+
+
+
+
+
+
+
+
+
+ Cancel
+ Submit
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/fancy-table/table-picker/table-picker.component.scss b/APP/src/components/fancy-table/table-picker/table-picker.component.scss
new file mode 100644
index 0000000..50270b1
--- /dev/null
+++ b/APP/src/components/fancy-table/table-picker/table-picker.component.scss
@@ -0,0 +1,8 @@
+.nbCard {
+ max-width: 1080px;
+ max-height: 99vh;
+ height: calc(70vh - 20px);
+}
+.tableContainer {
+ height: calc(50vh - 20px);
+}
diff --git a/APP/src/components/fancy-table/table-picker/table-picker.component.spec.ts b/APP/src/components/fancy-table/table-picker/table-picker.component.spec.ts
new file mode 100644
index 0000000..8621a48
--- /dev/null
+++ b/APP/src/components/fancy-table/table-picker/table-picker.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { TablePickerComponent } from './table-picker.component';
+
+describe('TablePickerComponent', () => {
+ let component: TablePickerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ TablePickerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TablePickerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/fancy-table/table-picker/table-picker.component.ts b/APP/src/components/fancy-table/table-picker/table-picker.component.ts
new file mode 100644
index 0000000..9c05f88
--- /dev/null
+++ b/APP/src/components/fancy-table/table-picker/table-picker.component.ts
@@ -0,0 +1,92 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { NbDialogRef } from '@nebular/theme';
+import { ArrayUtils } from '../../../utilities/array-utils';
+import { FancyColumn } from '../fancy-row-column.model';
+import { FancySelection } from '../fancy-selection.model';
+import { FancySettings } from '../fancy-settings.model';
+import { FancyTableComponent } from '../fancy-table.component';
+
+export const TABLE_PICKER_COL_CHECK_NAME = 'uiChecked';
+const COL_ID = 'id';
+@Component({
+ selector: 'ngx-table-picker',
+ templateUrl: './table-picker.component.html',
+ styleUrls: ['./table-picker.component.scss']
+})
+export class TablePickerComponent implements OnInit {
+
+ @ViewChild(FancyTableComponent) fancyTable: FancyTableComponent;
+ title: string;
+ data: any[];
+ selectedIds: string[];
+ settings: FancySettings;
+ displaySubmitBtn: boolean;
+ datum: any;
+ cardCssClass: string = 'nbCard';
+ constructor(
+ private dlgRef: NbDialogRef
+ ) { }
+
+ ngOnInit() {
+ if (this.settings.multiselect && !this.settings.columns.find(c => c.name == TABLE_PICKER_COL_CHECK_NAME)) {
+
+ this.settings.columns.unshift(
+ new FancyColumn({ name: TABLE_PICKER_COL_CHECK_NAME, type: 'checkall', widthPx: 20 })
+ );
+ }
+ if (this.settings.dialogCssClass) {
+ this.cardCssClass = this.settings.dialogCssClass;
+ }
+ for (let i = 0; i < this.data.length; i++) {
+ const element = this.data[i];
+ element[TABLE_PICKER_COL_CHECK_NAME] = this.selectedIds.includes(element[COL_ID]);
+ }
+
+ setTimeout(() => {
+ if (!this.settings.multiselect) {
+ this.fancyTable.searchRowAndFocus(r => r[COL_ID] == this.selectedIds[0]);
+ }
+ }, 200);
+ }
+ submit() {
+ if (this.settings.multiselect) {
+ this.removeCheckboxCol();
+ this.dlgRef.close(this.data.filter(d => d[TABLE_PICKER_COL_CHECK_NAME]));
+ } else {
+ //let selectedId = this.fancyTable.getSelected().focusedRow[COL_ID];
+ this.dlgRef.close(this.fancyTable.getSelected().focusedRow);
+
+ }
+ }
+ close() {
+ this.removeCheckboxCol();
+ this.dlgRef.close(null);
+ }
+ removeCheckboxCol() {
+ if (this.settings.multiselect) {
+ this.settings.multiselect = false;
+ this.settings.columns.splice(0, 1);
+ }
+ }
+
+ doubleClickRow(select: FancySelection) {
+ if (this.displaySubmitBtn) {
+ if (!this.settings.multiselect) {
+ this.dlgRef.close(select.focusedRow);
+ } else {
+ this.data[select.index][TABLE_PICKER_COL_CHECK_NAME] = !this.data[select.index][TABLE_PICKER_COL_CHECK_NAME];
+ }
+ } else {
+ let editContextMenuItem = this.settings.contextMenuItems.find(c => c.id == 'edit');
+ if (editContextMenuItem) {
+ editContextMenuItem.callback(select.focusedRow, null);
+ }
+ }
+
+ }
+ selectRow(select: FancySelection) {
+ if (select) {
+ this.datum = select.focusedRow;
+ }
+ }
+}
diff --git a/APP/src/components/loading-spinner/loading-spinner.component.html b/APP/src/components/loading-spinner/loading-spinner.component.html
new file mode 100644
index 0000000..c83818a
--- /dev/null
+++ b/APP/src/components/loading-spinner/loading-spinner.component.html
@@ -0,0 +1,9 @@
+
+
\ No newline at end of file
diff --git a/APP/src/components/loading-spinner/loading-spinner.component.scss b/APP/src/components/loading-spinner/loading-spinner.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/loading-spinner/loading-spinner.component.spec.ts b/APP/src/components/loading-spinner/loading-spinner.component.spec.ts
new file mode 100644
index 0000000..8273775
--- /dev/null
+++ b/APP/src/components/loading-spinner/loading-spinner.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { LoadingSpinnerComponent } from './loading-spinner.component';
+
+describe('LoadingSpinnerComponent', () => {
+ let component: LoadingSpinnerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ LoadingSpinnerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(LoadingSpinnerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/loading-spinner/loading-spinner.component.ts b/APP/src/components/loading-spinner/loading-spinner.component.ts
new file mode 100644
index 0000000..dc71182
--- /dev/null
+++ b/APP/src/components/loading-spinner/loading-spinner.component.ts
@@ -0,0 +1,14 @@
+import { Component, OnInit, Input } from '@angular/core';
+
+@Component({
+ selector: 'loading-spinner',
+ templateUrl: './loading-spinner.component.html',
+ styleUrls: ['./loading-spinner.component.scss']
+})
+export class LoadingSpinnerComponent {
+
+ @Input() public isLoading: boolean = false;
+
+ constructor() { }
+
+}
diff --git a/APP/src/components/loading-spinner/loading-spinner.module.ts b/APP/src/components/loading-spinner/loading-spinner.module.ts
new file mode 100644
index 0000000..4116dee
--- /dev/null
+++ b/APP/src/components/loading-spinner/loading-spinner.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { NbSpinnerModule } from '@nebular/theme';
+import { LoadingSpinnerComponent } from './loading-spinner.component';
+
+const components = [LoadingSpinnerComponent,];
+
+@NgModule({
+ declarations: [...components],
+ imports: [
+ CommonModule,
+ NbSpinnerModule,
+ ],
+ exports: [...components],
+})
+export class LoadingSpinnerModule { }
diff --git a/APP/src/components/pager/pager.component.html b/APP/src/components/pager/pager.component.html
new file mode 100644
index 0000000..805bd24
--- /dev/null
+++ b/APP/src/components/pager/pager.component.html
@@ -0,0 +1,48 @@
+ 0">
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/pager/pager.component.scss b/APP/src/components/pager/pager.component.scss
new file mode 100644
index 0000000..f00e031
--- /dev/null
+++ b/APP/src/components/pager/pager.component.scss
@@ -0,0 +1,12 @@
+nb-icon {
+ // margin-top: -2px !important;
+ // margin-bottom: -2px !important;
+ margin: -2px -6px !important;
+}
+ul {
+ margin-top: 0;
+ margin-bottom: 0px;
+}
+.justify-content-center {
+ margin-bottom: 0 !important;
+}
diff --git a/APP/src/components/pager/pager.component.spec.ts b/APP/src/components/pager/pager.component.spec.ts
new file mode 100644
index 0000000..8dea758
--- /dev/null
+++ b/APP/src/components/pager/pager.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { PagerComponent } from './pager.component';
+
+describe('PagerComponent', () => {
+ let component: PagerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ PagerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PagerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/pager/pager.component.ts b/APP/src/components/pager/pager.component.ts
new file mode 100644
index 0000000..8afb91c
--- /dev/null
+++ b/APP/src/components/pager/pager.component.ts
@@ -0,0 +1,98 @@
+import { Component, Input, Output, EventEmitter, OnChanges, SimpleChanges } from '@angular/core';
+import { NumberUtils } from '../../utilities/number-utils';
+
+@Component({
+ selector: 'pager',
+ templateUrl: './pager.component.html',
+ styleUrls: ['./pager.component.scss']
+})
+export class PagerComponent implements OnChanges {
+
+ @Input() public alignJustify: boolean = true;
+ @Input() public page: number;
+ @Input() public pages: number[];
+ @Input() public toolTips: string[];
+ @Input() public minPage: number = 1;
+ @Input() public maxPage: number = Number.POSITIVE_INFINITY; // default no limit
+ @Input() public prefix: string = "Page";
+ @Input() public numberOfPages: number = 5; // max number of pages to show at a time between "Previous" and "Next" buttons
+ @Output() public pageChange = new EventEmitter();
+ @Output() public addingNewPage = new EventEmitter();
+
+ pagesStart: number;
+ pagesEnd: number;
+
+ constructor() { }
+
+ public get lastPage(): number {
+ return this.pages[this.pages.length - 1];
+ }
+
+ ngOnChanges(change: SimpleChanges) {
+ if (false
+ || (change.pages && change.pages.currentValue !== change.pages.previousValue)
+ || (change.maxPage && change.maxPage.currentValue !== change.maxPage.previousValue)
+ ) {
+ if (this.pages.length > 0 && (this.page > Math.min(this.pages.length + 1, this.maxPage) || this.page < this.minPage)) {
+
+ this.page = NumberUtils.Clamp(this.page, this.minPage, Math.min(this.pages.length + 1, this.maxPage));
+ this.gotoPage(this.page);
+ // this.calculatePages();
+ }
+ }
+ if (false
+ || (change.page && change.page.currentValue !== change.page.previousValue)
+ || (change.maxPage && change.maxPage.currentValue !== change.maxPage.previousValue)
+ ) {
+ if (this.pages.length > 0) {
+
+ this.page = NumberUtils.Clamp(this.page, this.minPage, Math.min(this.pages.length + 1, this.maxPage));
+ //
+ this.calculatePages();
+ }
+ }
+ }
+
+ gotoPage(p: number) {
+ //
+ this.pageChange.emit(p);
+ }
+
+ getPageToolTip(page: number): string {
+ if (this.toolTips && this.toolTips.length > (page - 1)) {
+ return this.toolTips[(page - 1)];
+ }
+ return `Page ${page}`;
+ }
+
+ getNewPageToolTip(): string {
+ return this.page === this.pages[this.pages.length - 1] ? `New ${this.prefix}` : `Next ${this.prefix}`;
+ }
+
+ calculatePages() {
+ //
+ if (this.pages.length > 0) {
+
+ if (!this.pages.includes(this.page)) {
+ this.pages.push(this.page);
+ this.pages = this.pages.sort(NumberUtils.SortFunction);
+ }
+ let currentPage = this.page;
+ let lastPage = this.pages[this.pages.length - 1];
+ let pagesAfter = Math.floor(this.numberOfPages / 2);
+ let pagesBefore = this.numberOfPages - pagesAfter;
+ if (currentPage - pagesBefore < 0) {
+ pagesAfter += pagesBefore - currentPage;
+ }
+ if (currentPage + pagesAfter > lastPage) {
+ pagesBefore += currentPage + pagesAfter - lastPage;
+ }
+ this.pagesStart = Math.max(0, currentPage - pagesBefore);
+ this.pagesEnd = Math.min(lastPage, currentPage + pagesAfter);
+ }
+ }
+ addNewPage() {
+ if (this.page < this.maxPage)
+ this.addingNewPage.emit(this.page + 1);
+ }
+}
diff --git a/APP/src/components/pager/pager.module.ts b/APP/src/components/pager/pager.module.ts
new file mode 100644
index 0000000..bbcd0e6
--- /dev/null
+++ b/APP/src/components/pager/pager.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { PagerComponent } from './pager.component';
+import { NbIconModule, NbButtonModule, NbTooltipModule } from '@nebular/theme';
+
+const components = [
+ PagerComponent,
+]
+
+@NgModule({
+ declarations: [...components],
+ imports: [
+ CommonModule,
+
+ NbButtonModule,
+ NbIconModule,
+ NbTooltipModule,
+ ],
+ exports: [...components],
+})
+export class PagerModule { }
diff --git a/APP/src/components/password-input/password-input.component.html b/APP/src/components/password-input/password-input.component.html
new file mode 100644
index 0000000..544ee0b
--- /dev/null
+++ b/APP/src/components/password-input/password-input.component.html
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/APP/src/components/password-input/password-input.component.scss b/APP/src/components/password-input/password-input.component.scss
new file mode 100644
index 0000000..839f95d
--- /dev/null
+++ b/APP/src/components/password-input/password-input.component.scss
@@ -0,0 +1,19 @@
+:host {
+ flex: auto;
+ display: contents;
+}
+
+.input-group {
+ display: flex;
+ align-items: stretch;
+ width: 100%;
+
+ input {
+ flex: 1;
+ min-width: 0;
+ }
+
+ .input-group-append {
+ flex-shrink: 0;
+ }
+}
diff --git a/APP/src/components/password-input/password-input.component.spec.ts b/APP/src/components/password-input/password-input.component.spec.ts
new file mode 100644
index 0000000..7f94045
--- /dev/null
+++ b/APP/src/components/password-input/password-input.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule } from '@angular/forms';
+import { PasswordInputComponent } from './password-input.component';
+import { PasswordInputModule } from './password-input.module';
+
+describe('PasswordInputComponent', () => {
+ let component: PasswordInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [FormsModule, PasswordInputModule],
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(PasswordInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/password-input/password-input.component.ts b/APP/src/components/password-input/password-input.component.ts
new file mode 100644
index 0000000..1b88535
--- /dev/null
+++ b/APP/src/components/password-input/password-input.component.ts
@@ -0,0 +1,78 @@
+import {
+ ChangeDetectorRef,
+ Component,
+ EventEmitter,
+ forwardRef,
+ Input,
+ Output,
+} from '@angular/core';
+import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
+import { ControlContainer, NgForm } from '@angular/forms';
+import { UuidUtils } from '../../utilities/uuid-utils';
+
+@Component({
+ selector: 'rbj-password-input',
+ templateUrl: './password-input.component.html',
+ styleUrl: './password-input.component.scss',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => PasswordInputComponent),
+ multi: true,
+ },
+ ],
+ viewProviders: [{ provide: ControlContainer, useExisting: NgForm }],
+})
+export class PasswordInputComponent implements ControlValueAccessor {
+ private _value = '';
+ disabledState = false;
+ uuid: string = UuidUtils.generate();
+ /**
+ *
+ */
+ constructor(
+ private cdRef: ChangeDetectorRef,
+ ) {
+
+ }
+ @Input() id = '';
+ @Input() placeholder = 'Passphrase';
+ text: string = '';
+
+
+ onChange = (value: string) => { };
+ onTouched = () => { };
+ @Output() blur = new EventEmitter();
+
+ fieldTextType = false;
+
+ toggleFieldTextType(): void {
+ this.fieldTextType = !this.fieldTextType;
+ }
+
+ writeValue(value: string): void {
+ if (value != this.lastBlurValue) {
+ this.text = value;
+ this.lastBlurValue = value;
+ }
+ }
+
+ registerOnChange(fn: (value: string) => void): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this.disabledState = isDisabled;
+ }
+
+ lastBlurValue: string = '';
+ onBlur() {
+ if (this.lastBlurValue != this.text) {
+ this.lastBlurValue = this.text;
+ this.blur.emit(this.text);
+ }
+ }
+}
diff --git a/APP/src/components/password-input/password-input.module.ts b/APP/src/components/password-input/password-input.module.ts
new file mode 100644
index 0000000..94d4aa9
--- /dev/null
+++ b/APP/src/components/password-input/password-input.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { NbInputModule, NbButtonModule, NbIconModule, NbTooltipModule } from '@nebular/theme';
+import { PasswordInputComponent } from './password-input.component';
+
+@NgModule({
+ declarations: [PasswordInputComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ NbButtonModule,
+ NbIconModule,
+ NbTooltipModule,
+ ],
+ exports: [PasswordInputComponent],
+})
+export class PasswordInputModule {}
diff --git a/APP/src/components/phone-input/phone-input.component.html b/APP/src/components/phone-input/phone-input.component.html
new file mode 100644
index 0000000..3767081
--- /dev/null
+++ b/APP/src/components/phone-input/phone-input.component.html
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/APP/src/components/phone-input/phone-input.component.scss b/APP/src/components/phone-input/phone-input.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/phone-input/phone-input.component.spec.ts b/APP/src/components/phone-input/phone-input.component.spec.ts
new file mode 100644
index 0000000..cfc26cf
--- /dev/null
+++ b/APP/src/components/phone-input/phone-input.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { PhoneInputComponent } from './phone-input.component';
+
+describe('PhoneInputComponent', () => {
+ let component: PhoneInputComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ PhoneInputComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PhoneInputComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/phone-input/phone-input.component.ts b/APP/src/components/phone-input/phone-input.component.ts
new file mode 100644
index 0000000..44758f9
--- /dev/null
+++ b/APP/src/components/phone-input/phone-input.component.ts
@@ -0,0 +1,146 @@
+import { Component, OnInit, ChangeDetectionStrategy, forwardRef, ViewChild, ElementRef, Output, Input, EventEmitter, Renderer2 } from '@angular/core';
+import { NG_VALUE_ACCESSOR, ControlContainer, NgForm, NgModel } from '@angular/forms';
+import { ApplyMaskService } from '../../services/apply-mask.service';
+import { Util } from 'leaflet';
+import { StringUtils } from '../../utilities/string-utils';
+import { UuidUtils } from '../../utilities/uuid-utils';
+import { NbPopoverDirective } from '@nebular/theme';
+@Component({
+ selector: 'rbj-phone-input',
+ templateUrl: './phone-input.component.html',
+ styleUrls: ['./phone-input.component.scss'],
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => PhoneInputComponent),
+ multi: true
+ }
+ ],
+ viewProviders: [{ provide: ControlContainer, useExisting: NgForm }]
+})
+export class PhoneInputComponent implements OnInit {
+
+ @ViewChild('inputBox', { static: true }) input: NgModel;
+ @ViewChild(NbPopoverDirective) popover: NbPopoverDirective;
+ private _value: string;
+ private _initialized: boolean;
+ private _legalInput: boolean = false;
+ private _allowExtension: boolean = true;
+ private _lastBlurValue: string;
+ _readOnly: boolean;
+
+
+ onChange = (value: string) => { };
+ onTouched = () => { };
+ uuid: string = UuidUtils.generate();
+ name: string;
+ outputWithSymbol: boolean = true;
+ isValid: boolean = true;
+
+
+ @Input() id?: string = ''
+ @Input() placeholder: string;
+ @Input() class: string;
+ @Input() size: string = 'medium';
+ @Input() maskExpression: string = '(000) 000-0000';
+ @Input() extensionMask: string = ' ??????????';
+ _disabled = false;
+ @Input()
+ public set readonly(value) {
+ this._readOnly = typeof value !== 'undefined' && value !== false;
+ }
+ public get readonly(): boolean {
+ return this._readOnly;
+ }
+
+ @Input()
+ public set allowExtension(value) {
+ this._allowExtension = typeof value !== 'undefined' && value !== false;
+ }
+ public get allowExtension(): boolean {
+ return this._allowExtension;
+ }
+
+
+ @Output() inputChange = new EventEmitter();
+ @Output() focus = new EventEmitter();
+
+ constructor(
+ private renderer: Renderer2,
+ private el: ElementRef, private applyMaskService: ApplyMaskService) { }
+
+ ngAfterViewInit() {
+ this.renderer.removeAttribute(this.el.nativeElement, 'id')
+ }
+ //#region Implements
+ writeValue(value: string): void {
+ if (value) {
+ if (value == "( ) -") value = "";
+
+ this.value = this.outputWithSymbol ? value : value.replace(/[^\d]/g, '');
+ //this.setDisplayFormat(this.value);
+ } else {
+ this.value = null;
+ }
+ this._lastBlurValue = this.value;
+ //this.onChange(this.value);
+ }
+ registerOnChange(fn: (value: string) => void): void {
+ this.onChange = fn;
+ }
+ registerOnTouched(fn: any): void {
+ this.onTouched = fn;
+ }
+ setDisabledState?(isDisabled: boolean): void {
+ this._disabled = isDisabled;
+ }
+ //#endregion
+
+
+ public get value(): string {
+ return this._value;
+ }
+ public set value(v: string) {
+
+ if (this._initialized) {
+ if (v != this._value) {
+ this._value = v;
+ }
+ } else {
+ this._initialized = true;
+ this._value = v;
+ }
+ }
+
+ onFocus() {
+
+ if (this.input.invalid) {
+
+ this.popover.show();
+ } else {
+
+ this.popover.hide();
+ }
+ }
+ onFocusOut() {
+ if (this.input.invalid) {
+ setTimeout(() => {
+ this.el.nativeElement.querySelector('input').focus();
+ }, 200);
+ } else {
+
+ this.popover.hide();
+ }
+ }
+ ShowPopover() {
+ }
+
+ ngOnInit() {
+ }
+ onBlur() {
+ if (this._lastBlurValue != this.value) {
+ this.onChange(this.value);
+ this._lastBlurValue = this.value;
+ }
+ }
+}
diff --git a/APP/src/components/phone-input/phone-input.module.ts b/APP/src/components/phone-input/phone-input.module.ts
new file mode 100644
index 0000000..60293cf
--- /dev/null
+++ b/APP/src/components/phone-input/phone-input.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { PhoneInputComponent } from './phone-input.component';
+import { FormsModule } from '@angular/forms';
+import { NbInputModule, NbPopoverModule, NbIconModule } from '@nebular/theme';
+import { MaskDirectiveModule } from '../../directives/mask-directive/mask-directive.module';
+
+@NgModule({
+ declarations: [PhoneInputComponent],
+ imports: [
+ CommonModule,
+ FormsModule,
+ NbInputModule,
+ MaskDirectiveModule,
+ NbPopoverModule,
+ NbIconModule
+ ],
+ exports: [PhoneInputComponent]
+})
+export class PhoneInputModule { }
diff --git a/APP/src/components/pipes/ordinal.pipe.ts b/APP/src/components/pipes/ordinal.pipe.ts
new file mode 100644
index 0000000..5ca7b50
--- /dev/null
+++ b/APP/src/components/pipes/ordinal.pipe.ts
@@ -0,0 +1,11 @@
+import { Pipe, PipeTransform } from '@angular/core';
+import { NumberUtils } from '../../utilities/number-utils';
+
+@Pipe({
+ name: 'ordinal',
+})
+export class OrdinalPipe implements PipeTransform {
+ transform(value: number): string {
+ return NumberUtils.Ordinal(value);
+ }
+}
diff --git a/APP/src/components/popover-msg/popover-msg.component.html b/APP/src/components/popover-msg/popover-msg.component.html
new file mode 100644
index 0000000..53bc4ba
--- /dev/null
+++ b/APP/src/components/popover-msg/popover-msg.component.html
@@ -0,0 +1,10 @@
+
+
+
+
+ !
+ {{context.message}}
+
+
+
+
\ No newline at end of file
diff --git a/APP/src/components/popover-msg/popover-msg.component.scss b/APP/src/components/popover-msg/popover-msg.component.scss
new file mode 100644
index 0000000..d934706
--- /dev/null
+++ b/APP/src/components/popover-msg/popover-msg.component.scss
@@ -0,0 +1,61 @@
+$arrow-width: 12;
+$inner-arrow-width: 11;
+$border-color: #808080;
+:host {
+ .arrow {
+ width: 0;
+ height: 0;
+ margin-left: $arrow-width - 2 + px;
+ z-index: 50;
+ border-left: $arrow-width + px solid transparent;
+ border-right: $arrow-width + px solid transparent;
+
+ &.up {
+ border-bottom: $arrow-width + px solid $border-color;
+ }
+ &.down {
+ border-top: $arrow-width + px solid $border-color;
+ margin-top: -1px;
+ }
+
+ &:before {
+ height: $inner-arrow-width + px;
+ display: block;
+ width: $inner-arrow-width + px;
+ border-left: $inner-arrow-width + px solid transparent;
+ border-right: $inner-arrow-width + px solid transparent;
+ margin-left: -$inner-arrow-width + px;
+ position: absolute;
+ content: "";
+ }
+
+ &.up:before {
+ border-bottom: $inner-arrow-width + px solid white;
+ top: 1px;
+ }
+
+ &.down:before {
+ border-top: $inner-arrow-width + px solid white;
+ bottom: 1px;
+ }
+ }
+ .box {
+ background: white;
+ border-radius: 0.25rem;
+ -webkit-box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.25);
+ -moz-box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.25);
+ box-shadow: 3px 3px 6px 0px rgba(0, 0, 0, 0.25);
+ border: solid $border-color;
+ border-width: 1px;
+ margin-top: -1px;
+ }
+ .symbol {
+ border-radius: 0.2rem;
+ background-color: #ffa300;
+ color: white;
+ padding: 1px 10px;
+ font-size: 16px;
+ font-weight: 900;
+ margin-right: 10px;
+ }
+}
diff --git a/APP/src/components/popover-msg/popover-msg.component.spec.ts b/APP/src/components/popover-msg/popover-msg.component.spec.ts
new file mode 100644
index 0000000..a2ea732
--- /dev/null
+++ b/APP/src/components/popover-msg/popover-msg.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { PopoverMsgComponent } from './popover-msg.component';
+
+describe('PopoverMsgComponent', () => {
+ let component: PopoverMsgComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ PopoverMsgComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(PopoverMsgComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/popover-msg/popover-msg.component.ts b/APP/src/components/popover-msg/popover-msg.component.ts
new file mode 100644
index 0000000..5279450
--- /dev/null
+++ b/APP/src/components/popover-msg/popover-msg.component.ts
@@ -0,0 +1,26 @@
+import { Component, OnInit, ChangeDetectionStrategy, Input } from '@angular/core';
+import { NbPositionedContainerComponent, NbRenderableContainer, NbPosition } from '@nebular/theme';
+
+@Component({
+ selector: 'ngx-popover-msg',
+ templateUrl: './popover-msg.component.html',
+ styleUrls: ['./popover-msg.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class PopoverMsgComponent extends NbPositionedContainerComponent implements NbRenderableContainer {
+ renderContent() {
+ //throw new Error("Method not implemented.");
+ }
+
+ @Input() message: string
+
+ @Input()
+ context: { message?: string, position: NbPosition } = {
+ position: NbPosition.BOTTOM_END
+ };
+ ngOnInit() {
+ this.message = this.context.message;
+ this.position = this.context.position || NbPosition.BOTTOM_END;
+ }
+
+}
diff --git a/APP/src/components/popover-msg/popover-msg.module.ts b/APP/src/components/popover-msg/popover-msg.module.ts
new file mode 100644
index 0000000..5cd4eb7
--- /dev/null
+++ b/APP/src/components/popover-msg/popover-msg.module.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { PopoverMsgComponent } from './popover-msg.component';
+
+@NgModule({
+ declarations: [PopoverMsgComponent],
+ imports: [
+ CommonModule
+ ],
+ exports: [PopoverMsgComponent]
+})
+export class PopoverMsgModule { }
diff --git a/APP/src/components/rbj-md-editor/rbj-md-editor.component.html b/APP/src/components/rbj-md-editor/rbj-md-editor.component.html
new file mode 100644
index 0000000..79b78de
--- /dev/null
+++ b/APP/src/components/rbj-md-editor/rbj-md-editor.component.html
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/APP/src/components/rbj-md-editor/rbj-md-editor.component.scss b/APP/src/components/rbj-md-editor/rbj-md-editor.component.scss
new file mode 100644
index 0000000..e69de29
diff --git a/APP/src/components/rbj-md-editor/rbj-md-editor.component.spec.ts b/APP/src/components/rbj-md-editor/rbj-md-editor.component.spec.ts
new file mode 100644
index 0000000..87cb473
--- /dev/null
+++ b/APP/src/components/rbj-md-editor/rbj-md-editor.component.spec.ts
@@ -0,0 +1,23 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { RbjMdEditorComponent } from './rbj-md-editor.component';
+
+describe('RbjMdEditorComponent', () => {
+ let component: RbjMdEditorComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [RbjMdEditorComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(RbjMdEditorComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/rbj-md-editor/rbj-md-editor.component.ts b/APP/src/components/rbj-md-editor/rbj-md-editor.component.ts
new file mode 100644
index 0000000..ba30679
--- /dev/null
+++ b/APP/src/components/rbj-md-editor/rbj-md-editor.component.ts
@@ -0,0 +1,96 @@
+import { AfterViewInit, Component, ElementRef, forwardRef, Input, OnDestroy, ViewChild } from '@angular/core';
+import { NG_VALUE_ACCESSOR } from '@angular/forms';
+import Editor from '@toast-ui/editor';
+@Component({
+ selector: 'rbj-md-editor',
+ standalone: true,
+ imports: [],
+ templateUrl: './rbj-md-editor.component.html',
+ styleUrl: './rbj-md-editor.component.scss',
+ providers: [
+ {
+ provide: NG_VALUE_ACCESSOR,
+ useExisting: forwardRef(() => RbjMdEditorComponent),
+ multi: true,
+ },
+ ],
+})
+export class RbjMdEditorComponent implements AfterViewInit, OnDestroy {
+ @ViewChild('host', { static: true }) host!: ElementRef;
+
+ /** Choose the starting mode (user can still toggle with UI if you add your own buttons) */
+ @Input() initialEditType: 'wysiwyg' | 'markdown' = 'wysiwyg';
+ @Input() height = '400px';
+
+ private editor?: Editor;
+ private _value = '';
+ private _disabled = false;
+
+ private onChange: (val: string) => void = () => { };
+ private onTouched: () => void = () => { };
+
+ ngAfterViewInit(): void {
+ this.editor = new Editor({
+ el: this.host.nativeElement,
+ height: this.height,
+ initialEditType: this.initialEditType,
+ previewStyle: 'vertical',
+ initialValue: this._value ?? '',
+ usageStatistics: false,
+ });
+
+ // Toast UI emits "change"
+ this.editor.on('change', () => {
+ const md = this.editor?.getMarkdown() ?? '';
+ this._value = md;
+ this.onChange(md);
+ this.onTouched();
+ });
+
+ this.applyDisabled();
+ }
+
+ ngOnDestroy(): void {
+ this.editor?.destroy();
+ }
+
+ // ---- ControlValueAccessor ----
+ writeValue(value: string | null): void {
+ this._value = value ?? '';
+ if (this.editor) {
+ // Avoid resetting cursor unless value really changed
+ const current = this.editor.getMarkdown();
+ if (current !== this._value) {
+ this.editor.setMarkdown(this._value, false);
+ }
+ }
+ }
+
+ registerOnChange(fn: (val: string) => void): void {
+ this.onChange = fn;
+ }
+
+ registerOnTouched(fn: () => void): void {
+ this.onTouched = fn;
+ }
+
+ setDisabledState(isDisabled: boolean): void {
+ this._disabled = isDisabled;
+ this.applyDisabled();
+ }
+
+ private applyDisabled(): void {
+ if (!this.editor) return;
+
+ // Toast UI doesn't have a perfect universal "disable" API across all versions;
+ // simplest is to toggle contenteditable.
+ const root = this.host.nativeElement;
+ root.querySelectorAll('[contenteditable]').forEach((el) => {
+ (el as HTMLElement).setAttribute('contenteditable', (!this._disabled).toString());
+ });
+
+ // Also block pointer events for toolbar etc.
+ root.style.pointerEvents = this._disabled ? 'none' : 'auto';
+ root.style.opacity = this._disabled ? '0.7' : '1';
+ }
+}
\ No newline at end of file
diff --git a/APP/src/components/reload-dummy/reload-dummy.component.spec.ts b/APP/src/components/reload-dummy/reload-dummy.component.spec.ts
new file mode 100644
index 0000000..b883442
--- /dev/null
+++ b/APP/src/components/reload-dummy/reload-dummy.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { ReloadDummyComponent } from './reload-dummy.component';
+
+describe('ReloadDummyComponent', () => {
+ let component: ReloadDummyComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ReloadDummyComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ReloadDummyComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/reload-dummy/reload-dummy.component.ts b/APP/src/components/reload-dummy/reload-dummy.component.ts
new file mode 100644
index 0000000..ae03bdb
--- /dev/null
+++ b/APP/src/components/reload-dummy/reload-dummy.component.ts
@@ -0,0 +1,11 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'reload-dummy',
+ template: '',
+})
+export class ReloadDummyComponent {
+
+ constructor() { }
+
+}
diff --git a/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.html b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.html
new file mode 100644
index 0000000..1a9f5cf
--- /dev/null
+++ b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.scss b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.scss
new file mode 100644
index 0000000..15ed15f
--- /dev/null
+++ b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.scss
@@ -0,0 +1,15 @@
+.resizableBoxContainer {
+ width: 100%;
+ //@at-root: 80vh;
+ overflow: hidden;
+}
+.boxHolder {
+ border-left: 1px solid rgba(221, 221, 221, 0.6);
+ -webkit-box-shadow: inset 2px 0 0 0 rgba(0, 0, 0, 0.3);
+ box-shadow: inset 2px 0 0 0 rgba(0, 0, 0, 0.3);
+ background-color: rgb(86, 202, 133);
+}
+
+.hidden {
+ display: none;
+}
diff --git a/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.spec.ts b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.spec.ts
new file mode 100644
index 0000000..187538f
--- /dev/null
+++ b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.spec.ts
@@ -0,0 +1,25 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { ResizableBoxContainerComponent } from './resizable-box-container.component';
+
+describe('ResizableBoxContainerComponent', () => {
+ let component: ResizableBoxContainerComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(waitForAsync(() => {
+ TestBed.configureTestingModule({
+ declarations: [ ResizableBoxContainerComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(ResizableBoxContainerComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.ts b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.ts
new file mode 100644
index 0000000..68d4cc8
--- /dev/null
+++ b/APP/src/components/resizable-box/resizable-box-container/resizable-box-container.component.ts
@@ -0,0 +1,217 @@
+import { Component, OnInit, ChangeDetectionStrategy, ContentChildren, QueryList, ElementRef, ViewChild, Input, Output, EventEmitter } from '@angular/core';
+import { ResizableBoxComponent, boxSpec, size } from '../resizable-box.component';
+
+@Component({
+ selector: 'rbj-resizable-box-container',
+ templateUrl: './resizable-box-container.component.html',
+ styleUrls: ['./resizable-box-container.component.scss']
+})
+export class ResizableBoxContainerComponent implements OnInit {
+
+ private _specList: boxSpec[];
+ private _enableDrag: boolean = false;
+
+ @ContentChildren(ResizableBoxComponent) boxes: QueryList;
+
+ @ViewChild('container', { static: true }) containerDiv: ElementRef;
+ @ViewChild('boxHolder', { static: true }) boxHolder: ResizableBoxComponent;
+
+ layoutChanged: EventEmitter