diff --git a/Dockerfile b/Dockerfile index d4b3a7a..3ce04bb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN npm ci && \ npm prune --omit=dev # ---------- Release ---------- -FROM nginx:1.29.1-alpine-slim +FROM nginx:1.29.2-alpine-slim # Copy nginx config file RUN rm -rf /usr/share/nginx/html/* && rm -rf /etc/nginx/nginx.conf diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index e694199..ac8d5c3 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -9,23 +9,23 @@ "version": "2.1.1", "license": "MIT", "dependencies": { - "@angular/animations": "~20.3.2", - "@angular/cdk": "~20.2.5", - "@angular/common": "~20.3.2", - "@angular/compiler": "~20.3.2", - "@angular/core": "~20.3.2", - "@angular/forms": "~20.3.2", - "@angular/material": "~20.2.5", - "@angular/platform-browser": "~20.3.2", - "@angular/platform-browser-dynamic": "~20.3.2", - "@angular/router": "~20.3.2", - "@angular/service-worker": "~20.3.2", - "@apollo/client": "^3.14.0", + "@angular/animations": "~20.3.7", + "@angular/cdk": "~20.2.10", + "@angular/common": "~20.3.7", + "@angular/compiler": "~20.3.7", + "@angular/core": "~20.3.7", + "@angular/forms": "~20.3.7", + "@angular/material": "~20.2.10", + "@angular/platform-browser": "~20.3.7", + "@angular/platform-browser-dynamic": "~20.3.7", + "@angular/router": "~20.3.7", + "@angular/service-worker": "~20.3.7", + "@apollo/client": "^4.0.7", "@ng-bootstrap/ng-bootstrap": "~19.0.1", "@types/grecaptcha": "^3.0.9", - "apollo-angular": "^11.0.0", - "chart.js": "^4.5.0", - "core-js": "^3.45.1", + "apollo-angular": "^12.1.0", + "chart.js": "^4.5.1", + "core-js": "^3.46.0", "graphql": "^16.11.0", "graphql-tag": "^2.12.6", "ngx-markdown": "^20.1.0", @@ -33,16 +33,16 @@ "tslib": "^2.8.1" }, "devDependencies": { - "@angular/build": "^20.3.3", - "@angular/cli": "^20.3.3", - "@angular/compiler-cli": "~20.3.2", - "@types/node": "^24.5.2", - "@types/react": "^19.1.15", - "angular-eslint": "20.3.0", - "eslint": "^9.36.0", + "@angular/build": "^20.3.7", + "@angular/cli": "^20.3.7", + "@angular/compiler-cli": "~20.3.7", + "@types/node": "^24.9.1", + "@types/react": "^19.2.2", + "angular-eslint": "20.4.0", + "eslint": "^9.38.0", "ts-node": "^10.9.2", - "typescript": "^5.9.2", - "typescript-eslint": "8.44.1" + "typescript": "^5.9.3", + "typescript-eslint": "8.46.2" } }, "node_modules/@algolia/abtesting": { @@ -268,13 +268,13 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.2003.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.3.tgz", - "integrity": "sha512-DOnGyv9g24vaDzf5koLOcVri1kYJIBD9UKiJWOWk4H5cFlcpTXQ+PilPmDq6A+X94Tt4MZHImmKsk6LLRPIwFg==", + "version": "0.2003.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.7.tgz", + "integrity": "sha512-NGHLfrNQNjwWwvyQomMM1AqRaqH3UU0TwySJh9XlSc9dC/roB5zD2NjLf98K4LfAIfHvDBwkQ+dMo3F556/Xuw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.3", + "@angular-devkit/core": "20.3.7", "rxjs": "7.8.2" }, "engines": { @@ -284,9 +284,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.3.tgz", - "integrity": "sha512-2T5mX2duLapZYPYmXUSUe9VW8Dhu10nVBVvEp31jSE6xvjbPM5mlsv6+fks1E4RjhzvaamY9bm3WgwYwNiEV5g==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.7.tgz", + "integrity": "sha512-psmcjwYcXve4sLrcdnARc15/Wfd3RpydbtLo9+mViNzk5HQ6L2eEztKl/2QVYMgzZVIa1GfhjwUllVCyLAv3sg==", "dev": true, "license": "MIT", "dependencies": { @@ -312,13 +312,13 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.3.tgz", - "integrity": "sha512-LDn39BjyQLAK/DaVamLElMtI0UoCZIs4jKcMEv8PJ/nnBmrYFHVavWPggeFWMycjeXsdX34Msiml88HZWlXypw==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.7.tgz", + "integrity": "sha512-DUxcQBPKO69p56ZgIdVfxWyLiSjdcUoD6BH9/nWHp0QiqRAR6GcXP4SFax76JPl2WsiCp4hHZ233Hf69AP1xew==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.3", + "@angular-devkit/core": "20.3.7", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", "ora": "8.2.0", @@ -331,9 +331,9 @@ } }, "node_modules/@angular-eslint/builder": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-20.3.0.tgz", - "integrity": "sha512-3XpWLdh+/K4+r0ChkKW00SXWyBA7ShMpE+Pt1XUmIu4srJgGRnt8e+kC4Syi+s2t5QS7PjlwRaelB1KfSMXZ5A==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-20.4.0.tgz", + "integrity": "sha512-65mekrXZOurc2K6Ft7/aISiW9vsGcSTKvBxQVXarySBh1jzEvYKnG3tmiYP/ApTh6GPKrDo/XgbW85T67s9UXg==", "dev": true, "license": "MIT", "dependencies": { @@ -346,21 +346,21 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-20.3.0.tgz", - "integrity": "sha512-QwuNnmRNr/uNj89TxknPbGcs5snX1w7RoJJPNAsfb2QGcHzUTQovS8hqm9kaDZdpUJDPP7jt7B6F0+EjrPAXRA==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-20.4.0.tgz", + "integrity": "sha512-u3I/yABCm+lda/AdnLKJnjdQp1i4BACgEKY9D6eKIgijcRtlvUc6Jq+43e1oPZLj+3DdrlABNcB8HsA/+RzikA==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-20.3.0.tgz", - "integrity": "sha512-7ghzGTiExrgTetDQ6IPP5uXSa94Xhtzp2VHCIa58EcUb7oMv06HWZ1Uss3xgFmACsLpN+vayKJIdFiboqaGVRA==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-20.4.0.tgz", + "integrity": "sha512-gSQO18QLHt46UFjDcxkGhuFMKl4sPdFDnCZRZDpZC+4OZQ64f+xazPOveSoK1o4ttjSulfyXslE+I9bESmR5Mw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.3.0", - "@angular-eslint/utils": "20.3.0", + "@angular-eslint/bundled-angular-compiler": "20.4.0", + "@angular-eslint/utils": "20.4.0", "ts-api-utils": "^2.1.0" }, "peerDependencies": { @@ -370,19 +370,19 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.3.0.tgz", - "integrity": "sha512-WMJDJfybOLCiN4QrOyrLl+Zt5F+A/xoDYMWTdn+LgACheLs2tguVQiwf+oCgHnHGcsTsulPYlRHldKBGZMgs4w==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.4.0.tgz", + "integrity": "sha512-AWXtpWfivSE3PIwTPkuACPww5qu8dn3p1nuGuk2M/3LoHJFAMVvH6y2toTqGSUSTKALSdYzGhxbRPyDy6aEzDw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.3.0", - "@angular-eslint/utils": "20.3.0", + "@angular-eslint/bundled-angular-compiler": "20.4.0", + "@angular-eslint/utils": "20.4.0", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, "peerDependencies": { - "@angular-eslint/template-parser": "20.3.0", + "@angular-eslint/template-parser": "20.4.0", "@typescript-eslint/types": "^7.11.0 || ^8.0.0", "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", @@ -390,29 +390,29 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.3.0.tgz", - "integrity": "sha512-4n92tHKIJm1PP+FjhnmO7AMpvKdRIoF+YgF38oUU7aMJqfZ3RXIhazMMxw2u3VU1MisKH766KSll++c4LgarVA==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.4.0.tgz", + "integrity": "sha512-VIJ1RW4wq4sMf6jVaKiUR0H28Oro7eb9SKVSL7ztef8qGR8BMFKpyJM9W5DZ1Q6RXYpC0E8Q4rKEiTe3K3KsBQ==", "dev": true, "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 20.0.0 < 21.0.0", "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", - "@angular-eslint/eslint-plugin": "20.3.0", - "@angular-eslint/eslint-plugin-template": "20.3.0", + "@angular-eslint/eslint-plugin": "20.4.0", + "@angular-eslint/eslint-plugin-template": "20.4.0", "ignore": "7.0.5", "semver": "7.7.2", "strip-json-comments": "3.1.1" } }, "node_modules/@angular-eslint/template-parser": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.3.0.tgz", - "integrity": "sha512-gB564h/kZ7siWvgHDETU++sk5e25qFfVaizLaa6KoBEYFP6dOCiedz15LTcA0TsXp0rGu6Z6zkl291iSM1qzDA==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.4.0.tgz", + "integrity": "sha512-5Vyo/VJ1DrIsAkudFpZj1f7CpCLYuiTzTQksHTiZE18iYsLKRkEC7y9S6+TiHrdD96rhNxL28Pz9FDU4lIBjkw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.3.0", + "@angular-eslint/bundled-angular-compiler": "20.4.0", "eslint-scope": "^8.0.2" }, "peerDependencies": { @@ -421,13 +421,13 @@ } }, "node_modules/@angular-eslint/utils": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.3.0.tgz", - "integrity": "sha512-7XOQeNXgyhznDwoP1TwPrCMq/uXKJHQgCVPFREkJGKbNf/jzNldB7iV1eqpBzUQIPEQFgfcDG67dexpMAq3N4g==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.4.0.tgz", + "integrity": "sha512-SkR4fdPc+40W/53JmF6Nz6EIXIxvoRzhOdUiHoBKr/6fWONQwm7Vq55vk11AdK/oKTDUQCJ84HExQw6mzFljtg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "20.3.0" + "@angular-eslint/bundled-angular-compiler": "20.4.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -436,9 +436,9 @@ } }, "node_modules/@angular/animations": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.2.tgz", - "integrity": "sha512-za7onSElEUbaI9iS8j7nKf8FjyvVng6wFsb2ZuHxr71dMgnYkqPfMu0KMP+mkZ3yUVc//7SllXcSkGBHShyCcw==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.7.tgz", + "integrity": "sha512-i655RaL0zmLE3OESUlDnRNBDRIMW/67nTQvMqP6V1cQ42l2+SMJtREsxmX6cWt55/qvvgeytAA6aBN4aerBl5A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -447,18 +447,18 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.3.2" + "@angular/core": "20.3.7" } }, "node_modules/@angular/build": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.3.tgz", - "integrity": "sha512-WhwAbovHAxDbNeR5jB2IS/SVs+yQg9NETFeJ5f7T3n/414ULkGOhXn+29i1rzwJhf1uqM9lsedcv2tKn1N24/A==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.7.tgz", + "integrity": "sha512-NHN5JNDqUc0Ux4IZPCe/fpFAnuRHujkxVfRHSqDFW5+jtj2JuW1XO6qlX+kDheFRlj/NvFgTpidKsE9IjpfMWQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2003.3", + "@angular-devkit/architect": "0.2003.7", "@babel/core": "7.28.3", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -476,12 +476,12 @@ "parse5-html-rewriting-stream": "8.0.0", "picomatch": "4.0.3", "piscina": "5.1.3", - "rolldown": "1.0.0-beta.38", + "rollup": "4.52.3", "sass": "1.90.0", "semver": "7.7.2", "source-map-support": "0.5.21", "tinyglobby": "0.2.14", - "vite": "7.1.5", + "vite": "7.1.11", "watchpack": "2.4.4" }, "engines": { @@ -500,7 +500,7 @@ "@angular/platform-browser": "^20.0.0", "@angular/platform-server": "^20.0.0", "@angular/service-worker": "^20.0.0", - "@angular/ssr": "^20.3.3", + "@angular/ssr": "^20.3.7", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^20.0.0", @@ -550,9 +550,9 @@ } }, "node_modules/@angular/cdk": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.5.tgz", - "integrity": "sha512-1cpR/5jeKXLR1D+PsEvRn0QhSWD3/AjtbugJF5nlx/7L90YXhNFCmNAxAkdFKSn4YIDoPwMHgvOpS7yb51wohQ==", + "version": "20.2.10", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.10.tgz", + "integrity": "sha512-d95C2r3JP11KCahouWmPaxswz/EE7Zn1k8ocoGt70jl33x42Sg96vAHeOpnQ4yfrdA4W7Q+eWB/NqqvAGCzOPQ==", "license": "MIT", "dependencies": { "parse5": "^8.0.0", @@ -565,19 +565,19 @@ } }, "node_modules/@angular/cli": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.3.tgz", - "integrity": "sha512-3c8xCklJ0C0T6ETSncAoXlOYNi3x7vLT3PS56rIaQ0jtlvD4Y+RQakd3+iffVAapvh/JB27WNor8pJRThLZ/jg==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.7.tgz", + "integrity": "sha512-hNurF7g/e9cDHFBRCKLPSmQJs0n28jZsC3sTl/XuWE8PYtv5egh2EuqrxdruYB5GdANpIqSQNgDGQJrKrk/XnQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.2003.3", - "@angular-devkit/core": "20.3.3", - "@angular-devkit/schematics": "20.3.3", + "@angular-devkit/architect": "0.2003.7", + "@angular-devkit/core": "20.3.7", + "@angular-devkit/schematics": "20.3.7", "@inquirer/prompts": "7.8.2", "@listr2/prompt-adapter-inquirer": "3.0.1", "@modelcontextprotocol/sdk": "1.17.3", - "@schematics/angular": "20.3.3", + "@schematics/angular": "20.3.7", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.35.0", "ini": "5.0.0", @@ -600,9 +600,9 @@ } }, "node_modules/@angular/common": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.2.tgz", - "integrity": "sha512-5V9AzLhCA1dNhF+mvihmdHoZHbEhIb1jNYRA1/JMheR+G7NR8Mznu6RmWaKSWZ4AJeSJN8rizWN2wpVPWTKjSQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.7.tgz", + "integrity": "sha512-uf8dXYTJbedk/wudkt2MfbtvN/T97aEZBtOTq8/IFQQZ3722rag6D+Cg76e5hBccROOn+ueGJX2gpxz02phTwA==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -611,14 +611,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.3.2", + "@angular/core": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.2.tgz", - "integrity": "sha512-5fSzkPmRomZ9H43c82FJWLwdOi7MICMimP1y1oYJZcUh3jYRhXUrQvD0jifdRVkkgKNjaZYlMr0NkrYQFgFong==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.7.tgz", + "integrity": "sha512-EouHO15dUsgnFArj0M25R8cOPVoUfiFYSt6iXnMO8+S4dY1fDEmbFqkW5smlP66HL5Gys59Nwb5inejfIWHrLw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -628,9 +628,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.2.tgz", - "integrity": "sha512-rLox2THiALVQqYGUaxZ6YD8qUoXIOGTw3s0tim9/U65GuXGRtYgG0ZQWYp3yjEBes0Ksx2/15eFPp1Ol4FdEKQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.7.tgz", + "integrity": "sha512-viZwWlwc1BAqryRJE0Wq2WgAxDaW9fuwtYHYrOWnIn9sy9KemKmR6RmU9VRydrwUROOlqK49R9+RC1wQ6sYwqA==", "license": "MIT", "dependencies": { "@babel/core": "7.28.3", @@ -650,7 +650,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.2", + "@angular/compiler": "20.3.7", "typescript": ">=5.8 <6.0" }, "peerDependenciesMeta": { @@ -660,9 +660,9 @@ } }, "node_modules/@angular/core": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.2.tgz", - "integrity": "sha512-88uPgs5LjtnywnQaZE2ShBb1wa8IuD6jWs4nc4feo32QdBc55tjebTBFJSHbi3mUVAp0eS4wI6ITo0YIb01H4g==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.7.tgz", + "integrity": "sha512-2UuYzC2A5SUtu33tYTN411Wk0WilA+2Uld/GP3O6mragw1O7v/M8pMFmbe9TR5Ah/abRJIocWGlNqeztZmQmrw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -671,7 +671,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.2", + "@angular/compiler": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" }, @@ -685,9 +685,9 @@ } }, "node_modules/@angular/forms": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.2.tgz", - "integrity": "sha512-ECIbtwc7n9fPbiZXZVaoZpSiOksgcNbZ27oUN9BT7EmoXRzBw6yDL2UX6Ig7pEKhQGyBkKB+TMerRwTDVkkCWg==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.7.tgz", + "integrity": "sha512-uOCGCoqXeAWIlQMWiIeed/W8g8h2tk91YemMI+Ce1VQ/36Xfft40Bouz4eKcvJV6kLXGygdpWjzFGz32CE+3Og==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -696,16 +696,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.2", - "@angular/core": "20.3.2", - "@angular/platform-browser": "20.3.2", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7", + "@angular/platform-browser": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/localize": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-20.3.2.tgz", - "integrity": "sha512-RZMHgLZV1Aka7rUKvQbg08Dn+dMyVBEGTlUS6/bTDoB1Xq2UE9L8YKmlnEDQyzveO5vTsPvZZQRL4iLc4IokzQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-20.3.7.tgz", + "integrity": "sha512-FYuuwU9ujiVT+0xjMIutaUT2PErV4AvxeAPWMlYRA1/yQxqn1VyNUd6kHPjAV+yrZg9Q0MDco2/c0Lh8rmAhSA==", "license": "MIT", "peer": true, "dependencies": { @@ -723,20 +723,20 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "20.3.2", - "@angular/compiler-cli": "20.3.2" + "@angular/compiler": "20.3.7", + "@angular/compiler-cli": "20.3.7" } }, "node_modules/@angular/material": { - "version": "20.2.5", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-20.2.5.tgz", - "integrity": "sha512-zgmHqPykH3InEsVmNSpcVicXLWcYKHIt9Nv/J86K3NZDw4/IQgpfujnr7IotLwc9VpgI4Cl7Jbo95tFVFQAYmw==", + "version": "20.2.10", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-20.2.10.tgz", + "integrity": "sha512-WkJfUu7KiQY2lqHjMZtEKBG653sPmky0nytTMASsfQ/xUs56W3CAAEOuKhSyCNKsNeFJZS/NgJnvlpRzcE5k6g==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/cdk": "20.2.5", + "@angular/cdk": "20.2.10", "@angular/common": "^20.0.0 || ^21.0.0", "@angular/core": "^20.0.0 || ^21.0.0", "@angular/forms": "^20.0.0 || ^21.0.0", @@ -745,9 +745,9 @@ } }, "node_modules/@angular/platform-browser": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.2.tgz", - "integrity": "sha512-d9XcT2UuWZCc0UOtkCcPEnMcOFKNczahamT/Izg3H9jLS3IcT6l0ry23d/Xf0DRwhLYQdOZiG7l8HMZ1sWPMOg==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.7.tgz", + "integrity": "sha512-AbLtyR7fVEGDYyrz95dP2pc69J5XIjLLsFNAuNQPzNX02WPoAxtrWrNY6UnTzGoSrCc5F52hiL2Uo6yPZTiJcg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -756,9 +756,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "20.3.2", - "@angular/common": "20.3.2", - "@angular/core": "20.3.2" + "@angular/animations": "20.3.7", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7" }, "peerDependenciesMeta": { "@angular/animations": { @@ -767,9 +767,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-20.3.2.tgz", - "integrity": "sha512-ehoV67Vxr3ZE8BJ3g7Q4ZLHo3qJVoDUDz/4UeCqmDeOnKxcdD53HTA/pgOO4QhKStUFbzgU19OQD4e6fkP8YoQ==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-20.3.7.tgz", + "integrity": "sha512-4TEPA12183cMeVCzuU/Rmuk5RuIgsunTbjgx0o+ymxvYyULOxKDlhZ4hGDKzmRCOu6s3ZeEs4XbgaLP6pK+Kxg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -778,16 +778,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.2", - "@angular/compiler": "20.3.2", - "@angular/core": "20.3.2", - "@angular/platform-browser": "20.3.2" + "@angular/common": "20.3.7", + "@angular/compiler": "20.3.7", + "@angular/core": "20.3.7", + "@angular/platform-browser": "20.3.7" } }, "node_modules/@angular/router": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.2.tgz", - "integrity": "sha512-+Crx6QpK00juoNU3A1vbVf4DQ7fduLe3DUdAob6a9Uj+IoWj2Ijd8zUWF8E0cfNNFotJ4Gost0lJORDvqKcC7A==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.7.tgz", + "integrity": "sha512-Lq7mCNcLP1npmNh2JlNEe02YS2jNnaLnCy/t//o+Qq0c6DGV78JRl7pHubiB2R6XXlgvOcZWg88v94Li+y85Iw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -796,16 +796,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "20.3.2", - "@angular/core": "20.3.2", - "@angular/platform-browser": "20.3.2", + "@angular/common": "20.3.7", + "@angular/core": "20.3.7", + "@angular/platform-browser": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/service-worker": { - "version": "20.3.2", - "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-20.3.2.tgz", - "integrity": "sha512-SdaJ61JrliZLHEQ7kY2L98FLsVcti9+GeKODJUsHpnS2dv9RVSmWKJSa01kLsdOY/6wc1h5EHwkTg1iGHK0aew==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-20.3.7.tgz", + "integrity": "sha512-q9Q77wBBqScRJJQ7T+F0RepMY543Hm0HCZGvOujT+vQNFK3aRlWLlYenOUIhq2vlLXOhszCt8e5dY7/R+1eRWw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" @@ -817,7 +817,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "20.3.2", + "@angular/core": "20.3.7", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -836,9 +836,9 @@ } }, "node_modules/@antfu/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-TMilPqXyii1AsiEii6l6ubRzbo76p6oshUSYPaKsmXDavyMLqjzVDkcp3pHp5ELMUNJHATcEOGxKTTsX9yYhGg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==", "license": "MIT", "optional": true, "funding": { @@ -846,30 +846,30 @@ } }, "node_modules/@apollo/client": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.14.0.tgz", - "integrity": "sha512-0YQKKRIxiMlIou+SekQqdCo0ZTHxOcES+K8vKB53cIDpwABNR0P0yRzPgsbgcj3zRJniD93S/ontsnZsCLZrxQ==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.0.7.tgz", + "integrity": "sha512-hZp/mKtAqM+g6buUnu6Wqtyc33QebvfdY0SE46xWea4lU1CxwI57VORy2N2vA9CoCRgYM4ELNXzr6nNErAdhfg==", "license": "MIT", + "workspaces": [ + "dist", + "codegen", + "scripts/codemods/ac3-to-ac4" + ], "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", "@wry/equality": "^0.5.6", "@wry/trie": "^0.5.0", "graphql-tag": "^2.12.6", - "hoist-non-react-statics": "^3.3.2", "optimism": "^0.18.0", - "prop-types": "^15.7.2", - "rehackt": "^0.1.0", - "symbol-observable": "^4.0.0", - "ts-invariant": "^0.10.3", - "tslib": "^2.3.0", - "zen-observable-ts": "^1.2.5" + "tslib": "^2.3.0" }, "peerDependencies": { - "graphql": "^15.0.0 || ^16.0.0", + "graphql": "^16.0.0", "graphql-ws": "^5.5.5 || ^6.0.3", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "react-dom": "^17.0.0 || ^18.0.0 || >=19.0.0-rc", + "rxjs": "^7.3.0", "subscriptions-transport-ws": "^0.9.0 || ^0.11.0" }, "peerDependenciesMeta": { @@ -902,9 +902,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -956,13 +956,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -1071,9 +1071,9 @@ } }, "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==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1102,12 +1102,12 @@ } }, "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.28.4" + "@babel/types": "^7.28.5" }, "bin": { "parser": "bin/babel-parser.js" @@ -1131,17 +1131,17 @@ } }, "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", + "@babel/generator": "^7.28.5", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", + "@babel/parser": "^7.28.5", "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", + "@babel/types": "^7.28.5", "debug": "^4.3.1" }, "engines": { @@ -1149,13 +1149,13 @@ } }, "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -1236,40 +1236,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@emnapi/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.5.0.tgz", - "integrity": "sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.1.0", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", - "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", - "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", - "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", @@ -1732,9 +1698,9 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { @@ -1742,13 +1708,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1781,19 +1747,22 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz", - "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.1.tgz", + "integrity": "sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.16.0" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", - "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.16.0.tgz", + "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1886,9 +1855,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.36.0.tgz", - "integrity": "sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==", + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.38.0.tgz", + "integrity": "sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==", "dev": true, "license": "MIT", "engines": { @@ -1899,9 +1868,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1909,13 +1878,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", - "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", + "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.15.2", + "@eslint/core": "^0.16.0", "levn": "^0.4.1" }, "engines": { @@ -2021,9 +1990,9 @@ } }, "node_modules/@inquirer/ansi": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.0.tgz", - "integrity": "sha512-JWaTfCxI1eTmJ1BIv86vUfjVatOdxwD0DAVKYevY8SazeUUZtW+tNbsdejVO1GYE0GXJW1N1ahmiC3TFd+7wZA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.1.tgz", + "integrity": "sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==", "dev": true, "license": "MIT", "engines": { @@ -2031,16 +2000,16 @@ } }, "node_modules/@inquirer/checkbox": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.2.4.tgz", - "integrity": "sha512-2n9Vgf4HSciFq8ttKXk+qy+GsyTXPV1An6QAwe/8bkbbqvG4VW1I/ZY1pNu2rf+h9bdzMLPbRSfcNxkHBy/Ydw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.0.tgz", + "integrity": "sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2078,15 +2047,15 @@ } }, "node_modules/@inquirer/core": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.2.2.tgz", - "integrity": "sha512-yXq/4QUnk4sHMtmbd7irwiepjB8jXU0kkFRL4nr/aDBA2mDz13cMakEWdDwX3eSCTkk03kwcndD1zfRAIlELxA==", + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.0.tgz", + "integrity": "sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", @@ -2106,15 +2075,15 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.20", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.20.tgz", - "integrity": "sha512-7omh5y5bK672Q+Brk4HBbnHNowOZwrb/78IFXdrEB9PfdxL3GudQyDk8O9vQ188wj3xrEebS2M9n18BjJoI83g==", + "version": "4.2.21", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.21.tgz", + "integrity": "sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", + "@inquirer/core": "^10.3.0", "@inquirer/external-editor": "^1.0.2", - "@inquirer/type": "^3.0.8" + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -2129,14 +2098,14 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.20.tgz", - "integrity": "sha512-Dt9S+6qUg94fEvgn54F2Syf0Z3U8xmnBI9ATq2f5h9xt09fs2IJXSCIXyyVHwvggKWFXEY/7jATRo2K6Dkn6Ow==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.21.tgz", + "integrity": "sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2174,9 +2143,9 @@ } }, "node_modules/@inquirer/figures": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.13.tgz", - "integrity": "sha512-lGPVU3yO9ZNqA7vTYz26jny41lE7yoQansmqdMLBEfqaGsmdg7V3W9mK9Pvb5IL4EVZ9GnSDGMO/cJXud5dMaw==", + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.14.tgz", + "integrity": "sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==", "dev": true, "license": "MIT", "engines": { @@ -2184,14 +2153,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.4.tgz", - "integrity": "sha512-cwSGpLBMwpwcZZsc6s1gThm0J+it/KIJ+1qFL2euLmSKUMGumJ5TcbMgxEjMjNHRGadouIYbiIgruKoDZk7klw==", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.2.5.tgz", + "integrity": "sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -2206,14 +2175,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.20.tgz", - "integrity": "sha512-bbooay64VD1Z6uMfNehED2A2YOPHSJnQLs9/4WNiV/EK+vXczf/R988itL2XLDGTgmhMF2KkiWZo+iEZmc4jqg==", + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.21.tgz", + "integrity": "sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -2228,15 +2197,15 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.20", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.20.tgz", - "integrity": "sha512-nxSaPV2cPvvoOmRygQR+h0B+Av73B01cqYLcr7NXcGXhbmsYfUb8fDdw2Us1bI2YsX+VvY7I7upgFYsyf8+Nug==", + "version": "4.0.21", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.21.tgz", + "integrity": "sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8" + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9" }, "engines": { "node": ">=18" @@ -2281,14 +2250,14 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.8.tgz", - "integrity": "sha512-CQ2VkIASbgI2PxdzlkeeieLRmniaUU1Aoi5ggEdm6BIyqopE9GuDXdDOj9XiwOqK5qm72oI2i6J+Gnjaa26ejg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.9.tgz", + "integrity": "sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2304,15 +2273,15 @@ } }, "node_modules/@inquirer/search": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.1.3.tgz", - "integrity": "sha512-D5T6ioybJJH0IiSUK/JXcoRrrm8sXwzrVMjibuPs+AgxmogKslaafy1oxFiorNI4s3ElSkeQZbhYQgLqiL8h6Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.0.tgz", + "integrity": "sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2328,16 +2297,16 @@ } }, "node_modules/@inquirer/select": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.3.4.tgz", - "integrity": "sha512-Qp20nySRmfbuJBBsgPU7E/cL62Hf250vMZRzYDcBHty2zdD1kKCnoDFWRr0WO2ZzaXp3R7a4esaVGJUx0E6zvA==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.0.tgz", + "integrity": "sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/ansi": "^1.0.0", - "@inquirer/core": "^10.2.2", - "@inquirer/figures": "^1.0.13", - "@inquirer/type": "^3.0.8", + "@inquirer/ansi": "^1.0.1", + "@inquirer/core": "^10.3.0", + "@inquirer/figures": "^1.0.14", + "@inquirer/type": "^3.0.9", "yoctocolors-cjs": "^2.1.2" }, "engines": { @@ -2353,9 +2322,9 @@ } }, "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==", + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.9.tgz", + "integrity": "sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==", "dev": true, "license": "MIT", "engines": { @@ -2647,9 +2616,9 @@ ] }, "node_modules/@mermaid-js/parser": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.2.tgz", - "integrity": "sha512-+PO02uGF6L6Cs0Bw8RpGhikVvMWEysfAyl27qTlroUB8jSWr1lL0Sf6zi78ZxlSnmgSY2AMMKVgghnN9jTtwkQ==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", "license": "MIT", "optional": true, "dependencies": { @@ -3111,19 +3080,6 @@ "node": ">= 10" } }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.5.tgz", - "integrity": "sha512-TBr9Cf9onSAS2LQ2+QHx6XcC6h9+RIzJgbqG3++9TUZSH204AwEy5jg3BTQ0VATsyoGj4ee49tN/y6rvaOOtcg==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.5.0", - "@emnapi/runtime": "^1.5.0", - "@tybys/wasm-util": "^0.10.1" - } - }, "node_modules/@ng-bootstrap/ng-bootstrap": { "version": "19.0.1", "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-19.0.1.tgz", @@ -3428,16 +3384,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@oxc-project/types": { - "version": "0.89.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.89.0.tgz", - "integrity": "sha512-yuo+ECPIW5Q9mSeNmCDC2im33bfKuwW18mwkaHMQh8KakHYDzj4ci/q7wxf2qS3dMlVVCIyrs3kFtH5LmnlYnw==", - "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", @@ -3792,251 +3738,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-beta.38.tgz", - "integrity": "sha512-AE3HFQrjWCKLFZD1Vpiy+qsqTRwwoil1oM5WsKPSmfQ5fif/A+ZtOZetF32erZdsR7qyvns6qHEteEsF6g6rsQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-beta.38.tgz", - "integrity": "sha512-RaoWOKc0rrFsVmKOjQpebMY6c6/I7GR1FBc25v7L/R7NlM0166mUotwGEv7vxu7ruXH4SJcFeVrfADFUUXUmmQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-beta.38.tgz", - "integrity": "sha512-Ymojqc2U35iUc8NFU2XX1WQPfBRRHN6xHcrxAf9WS8BFFBn8pDrH5QPvH1tYs3lDkw6UGGbanr1RGzARqdUp1g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-beta.38.tgz", - "integrity": "sha512-0ermTQ//WzSI0nOL3z/LUWMNiE9xeM5cLGxjewPFEexqxV/0uM8/lNp9QageQ8jfc/VO1OURsGw34HYO5PaL8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-beta.38.tgz", - "integrity": "sha512-GADxzVUTCTp6EWI52831A29Tt7PukFe94nhg/SUsfkI33oTiNQtPxyLIT/3oRegizGuPSZSlrdBurkjDwxyEUQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-beta.38.tgz", - "integrity": "sha512-SKO7Exl5Yem/OSNoA5uLHzyrptUQ8Hg70kHDxuwEaH0+GUg+SQe9/7PWmc4hFKBMrJGdQtii8WZ0uIz9Dofg5Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-beta.38.tgz", - "integrity": "sha512-SOo6+WqhXPBaShLxLT0eCgH17d3Yu1lMAe4mFP0M9Bvr/kfMSOPQXuLxBcbBU9IFM9w3N6qP9xWOHO+oUJvi8Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-beta.38.tgz", - "integrity": "sha512-yvsQ3CyrodOX+lcoi+lejZGCOvJZa9xTsNB8OzpMDmHeZq3QzJfpYjXSAS6vie70fOkLVJb77UqYO193Cl8XBQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-beta.38.tgz", - "integrity": "sha512-84qzKMwUwikfYeOuJ4Kxm/3z15rt0nFGGQArHYIQQNSTiQdxGHxOkqXtzPFqrVfBJUdxBAf+jYzR1pttFJuWyg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-beta.38.tgz", - "integrity": "sha512-QrNiWlce01DYH0rL8K3yUBu+lNzY+B0DyCbIc2Atan6/S6flxOL0ow5DLQvMamOI/oKhrJ4xG+9MkMb9dDHbLQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-beta.38.tgz", - "integrity": "sha512-fnLtHyjwEsG4/aNV3Uv3Qd1ZbdH+CopwJNoV0RgBqrcQB8V6/Qdikd5JKvnO23kb3QvIpP+dAMGZMv1c2PJMzw==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^1.0.5" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-beta.38.tgz", - "integrity": "sha512-19cTfnGedem+RY+znA9J6ARBOCEFD4YSjnx0p5jiTm9tR6pHafRfFIfKlTXhun+NL0WWM/M0eb2IfPPYUa8+wg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-ia32-msvc": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-1.0.0-beta.38.tgz", - "integrity": "sha512-HcICm4YzFJZV+fI0O0bFLVVlsWvRNo/AB9EfUXvNYbtAxakCnQZ15oq22deFdz6sfi9Y4/SagH2kPU723dhCFA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-beta.38.tgz", - "integrity": "sha512-4Qx6cgEPXLb0XsCyLoQcUgYBpfL0sjugftob+zhUH0EOk/NVCAIT+h0NJhY+jn7pFpeKxhNMqhvTNx3AesxIAQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.38.tgz", - "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==", - "dev": true, - "license": "MIT" - }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.52.3", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", @@ -4346,14 +4047,14 @@ ] }, "node_modules/@schematics/angular": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.3.tgz", - "integrity": "sha512-lqIP1pNKp8yaqd663R3graZWaTBjXH+Cl72BQl1Ghl7lFGReZJALr4GiSMiBR9r30Epklcw5TwOSi+Bs4UKmbw==", + "version": "20.3.7", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.7.tgz", + "integrity": "sha512-jR2LPJVGK6yzPTNXkGJZYtdeLGkNdqJhVow2E+ILt3pk/LZuT/iSdr9V4nArU9yysifGuJFTyZapVOYkEYaykg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "20.3.3", - "@angular-devkit/schematics": "20.3.3", + "@angular-devkit/core": "20.3.7", + "@angular-devkit/schematics": "20.3.7", "jsonc-parser": "3.3.1" }, "engines": { @@ -4494,17 +4195,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", - "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", @@ -4862,20 +4552,20 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.5.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz", - "integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==", + "version": "24.9.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.9.1.tgz", + "integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.12.0" + "undici-types": "~7.16.0" } }, "node_modules/@types/react": { - "version": "19.1.15", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.15.tgz", - "integrity": "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA==", - "devOptional": true, + "version": "19.2.2", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.2.tgz", + "integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==", + "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -4889,17 +4579,17 @@ "optional": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", - "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", + "integrity": "sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/type-utils": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/type-utils": "8.46.2", + "@typescript-eslint/utils": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -4913,22 +4603,22 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/parser": "^8.46.2", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", - "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.46.2.tgz", + "integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4" }, "engines": { @@ -4944,14 +4634,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", - "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.46.2.tgz", + "integrity": "sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.1", - "@typescript-eslint/types": "^8.44.1", + "@typescript-eslint/tsconfig-utils": "^8.46.2", + "@typescript-eslint/types": "^8.46.2", "debug": "^4.3.4" }, "engines": { @@ -4966,14 +4656,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", - "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.46.2.tgz", + "integrity": "sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1" + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4984,9 +4674,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", - "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.2.tgz", + "integrity": "sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==", "dev": true, "license": "MIT", "engines": { @@ -5001,15 +4691,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", - "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.46.2.tgz", + "integrity": "sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -5026,9 +4716,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", - "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.46.2.tgz", + "integrity": "sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==", "dev": true, "license": "MIT", "engines": { @@ -5040,16 +4730,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", - "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.2.tgz", + "integrity": "sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.1", - "@typescript-eslint/tsconfig-utils": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/project-service": "8.46.2", + "@typescript-eslint/tsconfig-utils": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/visitor-keys": "8.46.2", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -5069,16 +4759,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", - "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.46.2.tgz", + "integrity": "sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1" + "@typescript-eslint/scope-manager": "8.46.2", + "@typescript-eslint/types": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5093,13 +4783,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", - "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.2.tgz", + "integrity": "sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/types": "8.46.2", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -5323,19 +5013,19 @@ } }, "node_modules/angular-eslint": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.3.0.tgz", - "integrity": "sha512-MvmeFuPmJHRmfL1A9IMtZJEYaU6sF++saJgpsU7aOD6YDZCGJ0J6HxlJ/q7YRbWYuI1q+gF/qALxdnuwHYadSg==", + "version": "20.4.0", + "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.4.0.tgz", + "integrity": "sha512-kKMiFJF3LRcKEYQaHBPbMw5xH2UY8JWD9fVyEnOwOe18r7MGlY002JqbCYz4NsmSx3lGmQXnUH+Hibx4iQ0JkA==", "dev": true, "license": "MIT", "dependencies": { "@angular-devkit/core": ">= 20.0.0 < 21.0.0", "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", - "@angular-eslint/builder": "20.3.0", - "@angular-eslint/eslint-plugin": "20.3.0", - "@angular-eslint/eslint-plugin-template": "20.3.0", - "@angular-eslint/schematics": "20.3.0", - "@angular-eslint/template-parser": "20.3.0", + "@angular-eslint/builder": "20.4.0", + "@angular-eslint/eslint-plugin": "20.4.0", + "@angular-eslint/eslint-plugin-template": "20.4.0", + "@angular-eslint/schematics": "20.4.0", + "@angular-eslint/template-parser": "20.4.0", "@typescript-eslint/types": "^8.0.0", "@typescript-eslint/utils": "^8.0.0" }, @@ -5389,20 +5079,10 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/ansis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz", - "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - } - }, "node_modules/apollo-angular": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/apollo-angular/-/apollo-angular-11.0.0.tgz", - "integrity": "sha512-8esMQ7Y/avjodz5G0vMlPy58KHrMetzx/I6Xp1Zn1AnYhBbbv7cEc25fZrdMWKwi0c061Fux3HWfu8iCk1OpDg==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/apollo-angular/-/apollo-angular-12.1.0.tgz", + "integrity": "sha512-1taINB0asxUEyYgGjkiFWWx/Jxo7P7JJwqOWJMjD/hYLbfE2MmJtwM1ojsDneBGPGw4hUQEc/xXMCreXYk6b9w==", "license": "MIT", "dependencies": { "tslib": "^2.6.2" @@ -5412,9 +5092,9 @@ }, "peerDependencies": { "@angular/core": "^18.0.0 || ^19.0.0 || ^20.0.0", - "@apollo/client": "^3.13.1", + "@apollo/client": "^4.0.1", "graphql": "^16.0.0", - "rxjs": "^6.0.0 || ^7.0.0" + "rxjs": "^7.3.0" } }, "node_modules/arg": { @@ -5459,9 +5139,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.9.tgz", - "integrity": "sha512-hY/u2lxLrbecMEWSB0IpGzGyDyeoMFQhCvZd2jGFSE5I17Fh01sYUBPCJtkWERw7zrac9+cIghxm/ytJa2X8iA==", + "version": "2.8.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.20.tgz", + "integrity": "sha512-JMWsdF+O8Orq3EMukbUN1QfbLK9mX2CkUmQBcW2T0s8OmdAUL5LLM/6wFwSrqXzlXB13yhyK9gTKS1rIizOduQ==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" @@ -5552,9 +5232,9 @@ } }, "node_modules/browserslist": { - "version": "4.26.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.26.2.tgz", - "integrity": "sha512-ECFzp6uFOSB+dcZ5BK/IBaGWssbSYBHvuMeMt3MMFyhI0Z8SqGgEkBLARgpRH3hutIgPVsALcMwbDrJqPxQ65A==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", "funding": [ { "type": "opencollective", @@ -5571,11 +5251,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.8.3", - "caniuse-lite": "^1.0.30001741", - "electron-to-chromium": "^1.5.218", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" }, "bin": { "browserslist": "cli.js" @@ -5711,9 +5391,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001745", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001745.tgz", - "integrity": "sha512-ywt6i8FzvdgrrrGbr1jZVObnVv6adj+0if2/omv9cmR2oiZs30zL4DIyaptKcbOrBdOIc74QTMoJvSE2QHh5UQ==", + "version": "1.0.30001751", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001751.tgz", + "integrity": "sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==", "funding": [ { "type": "opencollective", @@ -5755,9 +5435,9 @@ "license": "MIT" }, "node_modules/chart.js": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.0.tgz", - "integrity": "sha512-aYeC/jDgSEx8SHWZvANYMioYMZ2KX02W6f6uVfyteuCGcadDLcYVHdfdygsTQkQ4TKn5lghoojAsPj5pu0SnvQ==", + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", + "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", "license": "MIT", "dependencies": { "@kurkle/color": "^0.3.0" @@ -6031,9 +5711,9 @@ } }, "node_modules/core-js": { - "version": "3.45.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz", - "integrity": "sha512-L4NPsJlCfZsPeXukyzHFlg/i7IIVwHSItR0wg0FLNqYClJ4MQYTYLbC7EkjKYRLZF2iof2MUgN0EGy7MdQFChg==", + "version": "3.46.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.46.0.tgz", + "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", "hasInstallScript": true, "license": "MIT", "funding": { @@ -6121,7 +5801,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/cytoscape": { @@ -6747,9 +6427,9 @@ } }, "node_modules/detect-libc": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", - "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", "optional": true, @@ -6812,9 +6492,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz", - "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true, "optionalDependencies": { @@ -6866,15 +6546,15 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.227", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.227.tgz", - "integrity": "sha512-ITxuoPfJu3lsNWUi2lBM2PaBPYgH3uqmxut5vmBxgYvyI4AlJ6P3Cai1O76mOrkJCBzq0IxWg/NtqOrpu/0gKA==", + "version": "1.5.240", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.240.tgz", + "integrity": "sha512-OBwbZjWgrCOH+g6uJsA2/7Twpas2OlepS9uvByJjR2datRDuKGYeD+nP8lBBks2qnB7bGJNHDUx7c/YLaT3QMQ==", "license": "ISC" }, "node_modules/emoji-regex": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.5.0.tgz", - "integrity": "sha512-lb49vf1Xzfx080OKA0o6l8DQQpV+6Vg95zyCJX9VB/BqKYlhG7N4wgROUUHRA+ZPUefLnteQOad7z1kT2bV7bg==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT" }, "node_modules/emoji-toolkit": { @@ -7067,25 +6747,24 @@ } }, "node_modules/eslint": { - "version": "9.36.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.36.0.tgz", - "integrity": "sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==", + "version": "9.38.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz", + "integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.3.1", - "@eslint/core": "^0.15.2", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.1", + "@eslint/core": "^0.16.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.36.0", - "@eslint/plugin-kit": "^0.3.5", + "@eslint/js": "9.38.0", + "@eslint/plugin-kit": "^0.4.0", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", @@ -7346,9 +7025,9 @@ } }, "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==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", "dev": true, "license": "Apache-2.0" }, @@ -7897,19 +7576,10 @@ "node": ">= 0.4" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, "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==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "dev": true, "license": "ISC", "dependencies": { @@ -8081,9 +7751,9 @@ } }, "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==", + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", "dev": true, "license": "MIT" }, @@ -8398,9 +8068,9 @@ "license": "MIT" }, "node_modules/katex": { - "version": "0.16.22", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.22.tgz", - "integrity": "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg==", + "version": "0.16.25", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", + "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" @@ -8727,18 +8397,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -8789,9 +8447,9 @@ } }, "node_modules/marked": { - "version": "16.3.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-16.3.0.tgz", - "integrity": "sha512-K3UxuKu6l6bmA5FUwYho8CfJBlsUWAooKtdGgMcERSpF7gcBUrCGsLH7wDaaNOzwq18JzSUDyoEb/YsrqMac3w==", + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.1.tgz", + "integrity": "sha512-ntROs7RaN3EvWfy3EZi14H4YxmT6A5YvywfhO+0pm+cH/dnSQRmdAmoFIc3B9aiwTehyk7pESH4ofyBY+V5hZg==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -9278,9 +8936,9 @@ "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==", + "version": "11.5.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-11.5.0.tgz", + "integrity": "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9382,9 +9040,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.21", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz", - "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==", + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.26.tgz", + "integrity": "sha512-S2M9YimhSjBSvYnlr5/+umAnPHE++ODwt5e2Ij6FoX45HA/s4vHdkDx1eax2pAPeAOqu4s9b7ppahsyEFdVqQA==", "license": "MIT" }, "node_modules/nopt": { @@ -9456,19 +9114,29 @@ } }, "node_modules/npm-packlist": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.2.tgz", - "integrity": "sha512-DrIWNiWT0FTdDRjGOYfEEZUNe1IzaSZ+up7qBTKnrQDySpdmuOQvytrqQlpK5QrCA4IThMvL4wTumqaa1ZvVIQ==", + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", "dev": true, "license": "ISC", "dependencies": { "ignore-walk": "^8.0.0", - "proc-log": "^5.0.0" + "proc-log": "^6.0.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", + "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", + "dev": true, + "license": "ISC", + "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", @@ -9594,6 +9262,7 @@ "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" @@ -9779,9 +9448,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz", + "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", "license": "MIT", "optional": true }, @@ -10168,17 +9837,6 @@ "node": ">=10" } }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -10283,12 +9941,6 @@ "node": ">= 0.10" } }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", @@ -10308,24 +9960,6 @@ "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", "license": "Apache-2.0" }, - "node_modules/rehackt": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/rehackt/-/rehackt-0.1.0.tgz", - "integrity": "sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } - } - }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", @@ -10419,40 +10053,6 @@ "license": "Unlicense", "optional": true }, - "node_modules/rolldown": { - "version": "1.0.0-beta.38", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.38.tgz", - "integrity": "sha512-58frPNX55Je1YsyrtPJv9rOSR3G5efUZpRqok94Efsj0EUa8dnqJV3BldShyI7A+bVPleucOtzXHwVpJRcR0kQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.89.0", - "@rolldown/pluginutils": "1.0.0-beta.38", - "ansis": "^4.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-beta.38", - "@rolldown/binding-darwin-arm64": "1.0.0-beta.38", - "@rolldown/binding-darwin-x64": "1.0.0-beta.38", - "@rolldown/binding-freebsd-x64": "1.0.0-beta.38", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.38", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.38", - "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.38", - "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.38", - "@rolldown/binding-linux-x64-musl": "1.0.0-beta.38", - "@rolldown/binding-openharmony-arm64": "1.0.0-beta.38", - "@rolldown/binding-wasm32-wasi": "1.0.0-beta.38", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.38", - "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.38", - "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.38" - } - }, "node_modules/rollup": { "version": "4.52.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", @@ -11151,15 +10751,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/tar": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", @@ -11324,18 +10915,6 @@ "node": ">=6.10" } }, - "node_modules/ts-invariant": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz", - "integrity": "sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==", - "license": "MIT", - "dependencies": { - "tslib": "^2.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -11430,9 +11009,9 @@ } }, "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -11444,16 +11023,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.44.1.tgz", - "integrity": "sha512-0ws8uWGrUVTjEeN2OM4K1pLKHK/4NiNP/vz6ns+LjT/6sqpaYzIVFajZb1fj/IDwpsrrHb3Jy0Qm5u9CPcKaeg==", + "version": "8.46.2", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.46.2.tgz", + "integrity": "sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.44.1", - "@typescript-eslint/parser": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1" + "@typescript-eslint/eslint-plugin": "8.46.2", + "@typescript-eslint/parser": "8.46.2", + "@typescript-eslint/typescript-estree": "8.46.2", + "@typescript-eslint/utils": "8.46.2" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -11475,9 +11054,9 @@ "optional": true }, "node_modules/undici-types": { - "version": "7.12.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz", - "integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, "license": "MIT" }, @@ -11518,9 +11097,9 @@ } }, "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==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", "funding": [ { "type": "opencollective", @@ -11610,9 +11189,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", "dev": true, "license": "MIT", "dependencies": { @@ -12032,21 +11611,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/zen-observable": { - "version": "0.8.15", - "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", - "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==", - "license": "MIT" - }, - "node_modules/zen-observable-ts": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz", - "integrity": "sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==", - "license": "MIT", - "dependencies": { - "zen-observable": "0.8.15" - } - }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/package.json b/package.json index 4a5950e..3875f36 100644 --- a/package.json +++ b/package.json @@ -24,23 +24,23 @@ "license": "MIT", "private": true, "dependencies": { - "@angular/animations": "~20.3.2", - "@angular/cdk": "~20.2.5", - "@angular/common": "~20.3.2", - "@angular/compiler": "~20.3.2", - "@angular/core": "~20.3.2", - "@angular/forms": "~20.3.2", - "@angular/material": "~20.2.5", - "@angular/platform-browser": "~20.3.2", - "@angular/platform-browser-dynamic": "~20.3.2", - "@angular/router": "~20.3.2", - "@angular/service-worker": "~20.3.2", - "@apollo/client": "^3.14.0", + "@angular/animations": "~20.3.7", + "@angular/cdk": "~20.2.10", + "@angular/common": "~20.3.7", + "@angular/compiler": "~20.3.7", + "@angular/core": "~20.3.7", + "@angular/forms": "~20.3.7", + "@angular/material": "~20.2.10", + "@angular/platform-browser": "~20.3.7", + "@angular/platform-browser-dynamic": "~20.3.7", + "@angular/router": "~20.3.7", + "@angular/service-worker": "~20.3.7", + "@apollo/client": "^4.0.7", "@ng-bootstrap/ng-bootstrap": "~19.0.1", "@types/grecaptcha": "^3.0.9", - "apollo-angular": "^11.0.0", - "chart.js": "^4.5.0", - "core-js": "^3.45.1", + "apollo-angular": "^12.1.0", + "chart.js": "^4.5.1", + "core-js": "^3.46.0", "graphql": "^16.11.0", "graphql-tag": "^2.12.6", "ngx-markdown": "^20.1.0", @@ -48,16 +48,16 @@ "tslib": "^2.8.1" }, "devDependencies": { - "@angular/build": "^20.3.3", - "@angular/cli": "^20.3.3", - "@angular/compiler-cli": "~20.3.2", - "@types/node": "^24.5.2", - "@types/react": "^19.1.15", - "angular-eslint": "20.3.0", - "eslint": "^9.36.0", + "@angular/build": "^20.3.7", + "@angular/cli": "^20.3.7", + "@angular/compiler-cli": "~20.3.7", + "@types/node": "^24.9.1", + "@types/react": "^19.2.2", + "angular-eslint": "20.4.0", + "eslint": "^9.38.0", "ts-node": "^10.9.2", - "typescript": "^5.9.2", - "typescript-eslint": "8.44.1" + "typescript": "^5.9.3", + "typescript-eslint": "8.46.2" }, "repository": { "type": "git", diff --git a/src/app/app.component.ts b/src/app/app.component.ts index aa5c0cd..e52dcd8 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -72,7 +72,7 @@ export class AppComponent implements OnInit, OnDestroy { this.authService.currentUser.subscribe(() => { this.loggedUserName = this.authService.getUserInfo("name"); const avatar = this.authService.getUserInfo("avatar"); - this.profileAvatar = avatar || "assets\\switcherapi_mark_white.png"; + this.profileAvatar = avatar || String.raw`assets\switcherapi_mark_white.png`; }); this.reloadTheme(); diff --git a/src/app/dashboard-module/domain-list/domain-list.component.ts b/src/app/dashboard-module/domain-list/domain-list.component.ts index d136bde..f1588ed 100644 --- a/src/app/dashboard-module/domain-list/domain-list.component.ts +++ b/src/app/dashboard-module/domain-list/domain-list.component.ts @@ -58,7 +58,7 @@ export class DomainListComponent implements OnInit, OnDestroy { createDomain(): void { const dialogRef = this.dialog.open(DomainCreateComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { name: '', description: '' } }); diff --git a/src/app/dashboard-module/domain-module/changelog/changelog.component.ts b/src/app/dashboard-module/domain-module/changelog/changelog.component.ts index 6da0ff3..7ccc55c 100644 --- a/src/app/dashboard-module/domain-module/changelog/changelog.component.ts +++ b/src/app/dashboard-module/domain-module/changelog/changelog.component.ts @@ -98,7 +98,7 @@ export class ChangelogComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -140,11 +140,11 @@ export class ChangelogComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { if (data.length) { - data.forEach(element => { + for (const element of data) { if (element.action === 'DELETE') { this.removable = element.result === 'ok'; } - }); + } } }); } @@ -308,17 +308,17 @@ export class ChangelogComponent implements OnInit, OnDestroy { } private customFilterPredicate(data: History, filter: string): boolean { - if (data.updatedBy.indexOf(filter) >= 0) { + if (data.updatedBy.includes(filter)) { return true; } - if (data.date.toString().indexOf(filter) >= 0) { + if (data.date.toString().includes(filter)) { return true; } - return Object.keys(data.newValue).filter(key => key.indexOf(filter) >= 0).length > 0 || - Object.values(data.newValue).filter(value => value.indexOf(filter) >= 0).length > 0 || - Object.values(data.oldValue).filter(value => value.indexOf(filter) >= 0).length > 0; + return Object.keys(data.newValue).some(key => key.includes(filter)) || + Object.values(data.newValue).some(value => String(value).includes(filter)) || + Object.values(data.oldValue).some(value => String(value).includes(filter)); } private compare(a: number | string, b: number | string, isAsc: boolean) { @@ -346,7 +346,7 @@ export class ChangelogComponent implements OnInit, OnDestroy { } getColumnLabel(dataColumn: string): string { - return this.columnsToDisplay.filter(column => column.data === dataColumn)[0].display; + return this.columnsToDisplay.find(column => column.data === dataColumn)?.display ?? ''; } getElementKeys(element: any): string[] { @@ -367,13 +367,13 @@ export class ChangelogComponent implements OnInit, OnDestroy { formatExpandedData(element: any): string { if (element instanceof Object) { - return element != undefined ? JSON.stringify(element) : ''; + return JSON.stringify(element); } return element; } formatDateContent(value: string): string { - if (window.screen.width < 380) { + if (globalThis.screen.width < 380) { return this.datepipe.transform(value, 'yy/MM/dd HH:mm'); } return value; diff --git a/src/app/dashboard-module/domain-module/common/element-autocomplete/element-autocomplete.component.ts b/src/app/dashboard-module/domain-module/common/element-autocomplete/element-autocomplete.component.ts index d5c29de..c31515a 100644 --- a/src/app/dashboard-module/domain-module/common/element-autocomplete/element-autocomplete.component.ts +++ b/src/app/dashboard-module/domain-module/common/element-autocomplete/element-autocomplete.component.ts @@ -5,7 +5,7 @@ import { ConsoleLogger } from 'src/app/_helpers/console-logger'; import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { DomainService } from 'src/app/services/domain.service'; import { Group } from 'src/app/model/group'; -import { ApolloQueryResult } from '@apollo/client'; +import { Apollo } from 'apollo-angular/apollo'; import { MatFormField, MatLabel, MatInput } from '@angular/material/input'; import { MatAutocompleteTrigger, MatAutocomplete, MatOption } from '@angular/material/autocomplete'; import { MatTooltip } from '@angular/material/tooltip'; @@ -33,7 +33,7 @@ export class ElementAutocompleteComponent implements OnInit, OnDestroy { smartSearchFormControl = new FormControl(''); searchListItems: any[] = []; searchedValues: Observable; - private query: Observable>; + private query: Observable>; ngOnInit() { this.searchedValues = this.smartSearchFormControl.valueChanges.pipe( diff --git a/src/app/dashboard-module/domain-module/components/components.component.ts b/src/app/dashboard-module/domain-module/components/components.component.ts index f53fa90..ffc1297 100644 --- a/src/app/dashboard-module/domain-module/components/components.component.ts +++ b/src/app/dashboard-module/domain-module/components/components.component.ts @@ -76,7 +76,7 @@ export class ComponentsComponent extends BasicComponent implements OnInit, OnDes }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -119,7 +119,7 @@ export class ComponentsComponent extends BasicComponent implements OnInit, OnDes .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { if (data.length) { - data.forEach(element => { + for (const element of data) { if (element.action === 'UPDATE') { this.updatable = element.result === 'ok'; } else if (element.action === 'DELETE') { @@ -127,7 +127,7 @@ export class ComponentsComponent extends BasicComponent implements OnInit, OnDes } else if (element.action === 'CREATE') { this.creatable = element.result === 'ok'; } - }); + } } }); } @@ -169,14 +169,14 @@ export class ComponentsComponent extends BasicComponent implements OnInit, OnDes modalConfirmation.componentInstance.question = `Are you sure you want to remove ${selectedEnvironment.name}?`; modalConfirmation.result.then((result) => { if (result) { - const component = this.components.filter(env => env.id === selectedEnvironment.id); + const component = this.components.find(env => env.id === selectedEnvironment.id); this.compService.deleteComponent(selectedEnvironment.id) .pipe(takeUntil(this.unsubscribe)) .subscribe({ next: env => { - if (env) { - this.components.splice(this.components.indexOf(component[0]), 1); + if (env && component) { + this.components.splice(this.components.indexOf(component), 1); this.toastService.showSuccess('Component removed with success'); } }, diff --git a/src/app/dashboard-module/domain-module/config/config-detail/config-detail.component.ts b/src/app/dashboard-module/domain-module/config/config-detail/config-detail.component.ts index fd73184..ab896e6 100644 --- a/src/app/dashboard-module/domain-module/config/config-detail/config-detail.component.ts +++ b/src/app/dashboard-module/domain-module/config/config-detail/config-detail.component.ts @@ -154,38 +154,38 @@ export class ConfigDetailComponent extends DetailComponent implements OnInit, On if (!this.editing) { this.classStatus = 'header editing'; this.editing = true; - } else { - const { valid } = this.keyFormControl; - - if (valid) { - this.setBlockUI(true, 'Saving changes...'); - this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; - - const body = { - key: this.keyElement.nativeElement.value, - description: this.descElement.nativeElement.value - }; - - if (super.validateEdition({ - key: this.config.key, - description: this.config.description, - components: String(this.config.components.map(component => component.name)), - disable_metrics: this.config.disable_metrics != undefined ? - this.config.disable_metrics[this.currentEnvironment] : false - }, - { - key: body.key, - description: body.description, - components: String(this.components), - disable_metrics: this.disableMetrics - })) { - this.setBlockUI(false); - this.editing = false; - return; - } + return; + } - this.editConfig(body); + const { valid } = this.keyFormControl; + + if (valid) { + this.setBlockUI(true, 'Saving changes...'); + this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; + + const body = { + key: this.keyElement.nativeElement.value, + description: this.descElement.nativeElement.value + }; + + if (super.validateEdition({ + key: this.config.key, + description: this.config.description, + components: String(this.config.components.map(component => component.name)), + disable_metrics: this.config.disable_metrics === undefined ? + false : this.config.disable_metrics[this.currentEnvironment] + }, { + key: body.key, + description: body.description, + components: String(this.components), + disable_metrics: this.disableMetrics + })) { + this.setBlockUI(false); + this.editing = false; + return; } + + this.editConfig(body); } } @@ -217,7 +217,7 @@ export class ConfigDetailComponent extends DetailComponent implements OnInit, On addStrategy() { const dialogRef = this.dialog.open(StrategyCreateComponent, { width: '700px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { currentStrategies: this.strategies.getValue() } @@ -486,8 +486,8 @@ export class ConfigDetailComponent extends DetailComponent implements OnInit, On private editConfig(body: { key: string; description: string; }): void { const updateDisableMetrics = this.getDisableMetricsChange(); this.configService.updateConfig(this.config.id, - body.key != this.config.key ? body.key : undefined, - body.description != this.config.description ? body.description : undefined, updateDisableMetrics) + body.key === this.config.key ? undefined : body.key, + body.description === this.config.description ? undefined : body.description, updateDisableMetrics) .pipe(takeUntil(this.unsubscribe)) .subscribe({ next: data => { diff --git a/src/app/dashboard-module/domain-module/config/config-list/config-list.component.ts b/src/app/dashboard-module/domain-module/config/config-list/config-list.component.ts index 851afd8..7c30557 100644 --- a/src/app/dashboard-module/domain-module/config/config-list/config-list.component.ts +++ b/src/app/dashboard-module/domain-module/config/config-list/config-list.component.ts @@ -84,7 +84,7 @@ export class ConfigListComponent extends ListComponent implements OnInit, OnDest createConfig(): void { const dialogRef = this.dialog.open(ConfigCreateComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { key: '', description: '' } }); diff --git a/src/app/dashboard-module/domain-module/config/config-preview/config-preview.component.ts b/src/app/dashboard-module/domain-module/config/config-preview/config-preview.component.ts index 839d858..eee26ab 100644 --- a/src/app/dashboard-module/domain-module/config/config-preview/config-preview.component.ts +++ b/src/app/dashboard-module/domain-module/config/config-preview/config-preview.component.ts @@ -113,7 +113,7 @@ export class ConfigPreviewComponent extends BasicComponent implements OnInit, On private readPermissionToObject(): void { this.loadOperationSelectionComponent(); - const element = this.permissions.filter(p => p.id === this.config.id)[0]; + const element = this.permissions.find(p => p.id === this.config.id); this.updatable = element.permissions.find(p => p.action === 'UPDATE').result === 'ok'; this.removable = element.permissions.find(p => p.action === 'DELETE').result === 'ok'; diff --git a/src/app/dashboard-module/domain-module/config/relay-detail/relay-detail.component.ts b/src/app/dashboard-module/domain-module/config/relay-detail/relay-detail.component.ts index b96bd06..2fb8f87 100644 --- a/src/app/dashboard-module/domain-module/config/relay-detail/relay-detail.component.ts +++ b/src/app/dashboard-module/domain-module/config/relay-detail/relay-detail.component.ts @@ -98,39 +98,40 @@ export class RelayDetailComponent extends DetailComponent implements OnInit, OnD if (!this.editing) { this.classStatus = 'header editing'; this.editing = true; - } else { - const { valid } = this.endpointFormControl; - if (!valid) { - this.toastService.showError(`Unable to save relay`); - return; - } - - this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; - - if (super.validateEdition( - { - type: this.config.relay.type, - method: this.config.relay.method, - description: this.config.relay.description, - endpoint: this.config.relay.endpoint[this.currentEnvironment], - auth_token: this.getRelayAttribute('auth_token') ? this.config.relay.auth_token[this.currentEnvironment] : '', - auth_prefix: this.config.relay.auth_prefix || '' - }, - { - type: this.relayTypeFormControl.value, - method: this.relayMethodFormControl.value, - description: this.descElement.nativeElement.value, - endpoint: this.endpointFormControl.value, - auth_token: this.authTokenElement.nativeElement.value, - auth_prefix: this.authPrefixElement.nativeElement.value - })) { - this.setBlockUI(false); - this.editing = false; - return; - } + return; + } + + const { valid } = this.endpointFormControl; + if (!valid) { + this.toastService.showError(`Unable to save relay`); + return; + } - this.editRelay(); + this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; + + if (super.validateEdition( + { + type: this.config.relay.type, + method: this.config.relay.method, + description: this.config.relay.description, + endpoint: this.config.relay.endpoint[this.currentEnvironment], + auth_token: this.getRelayAttribute('auth_token') ? this.config.relay.auth_token[this.currentEnvironment] : '', + auth_prefix: this.config.relay.auth_prefix || '' + }, + { + type: this.relayTypeFormControl.value, + method: this.relayMethodFormControl.value, + description: this.descElement.nativeElement.value, + endpoint: this.endpointFormControl.value, + auth_token: this.authTokenElement.nativeElement.value, + auth_prefix: this.authPrefixElement.nativeElement.value + })) { + this.setBlockUI(false); + this.editing = false; + return; } + + this.editRelay(); } verifyRelay(): void { diff --git a/src/app/dashboard-module/domain-module/config/strategy-create/strategy-create.component.ts b/src/app/dashboard-module/domain-module/config/strategy-create/strategy-create.component.ts index acd5ce3..058651d 100644 --- a/src/app/dashboard-module/domain-module/config/strategy-create/strategy-create.component.ts +++ b/src/app/dashboard-module/domain-module/config/strategy-create/strategy-create.component.ts @@ -154,9 +154,7 @@ export class StrategyCreateComponent implements OnInit, OnDestroy { private loadAvailableStrategies(): void { const currentStrategies: Strategy[] = this.data.currentStrategies - currentStrategies.forEach(strategy => { - this.strategies.splice(this.strategies.indexOf(strategy.strategy), 1); - }) + currentStrategies.forEach(strategy => this.strategies.splice(this.strategies.indexOf(strategy.strategy), 1)); } private loadOperations(strategySelected: string): void { diff --git a/src/app/dashboard-module/domain-module/config/strategy-detail/strategy-detail.component.ts b/src/app/dashboard-module/domain-module/config/strategy-detail/strategy-detail.component.ts index 13fc410..246e268 100644 --- a/src/app/dashboard-module/domain-module/config/strategy-detail/strategy-detail.component.ts +++ b/src/app/dashboard-module/domain-module/config/strategy-detail/strategy-detail.component.ts @@ -94,9 +94,9 @@ export class StrategyDetailComponent extends DetailComponent implements OnInit, @HostListener('window:resize') onResize() { this.strategyValuesLength = 30; - if (window.innerWidth < 1200 && window.innerWidth > 770) { + if (globalThis.innerWidth < 1200 && globalThis.innerWidth > 770) { this.strategyValuesLength = 50; - } else if (window.innerWidth < 770) { + } else if (globalThis.innerWidth < 770) { this.strategyValuesLength = 20; } } @@ -106,24 +106,25 @@ export class StrategyDetailComponent extends DetailComponent implements OnInit, this.loadStrategyRequirements(); this.classStatus = 'header editing'; this.editing = true; - } else { - this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; - - const body = { - operation: this.operationCategoryFormControl.value, - description: this.descElement.nativeElement.value - }; - - if (super.validateEdition( - { operation: this.strategy.operation, description: this.strategy.description }, - { operation: body.operation, description: body.description})) { - this.setBlockUI(false); - this.editing = false; - return; - } + return; + } - this.editStrategy(body); + this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; + + const body = { + operation: this.operationCategoryFormControl.value, + description: this.descElement.nativeElement.value + }; + + if (super.validateEdition( + { operation: this.strategy.operation, description: this.strategy.description }, + { operation: body.operation, description: body.description})) { + this.setBlockUI(false); + this.editing = false; + return; } + + this.editStrategy(body); } delete() { @@ -157,7 +158,7 @@ export class StrategyDetailComponent extends DetailComponent implements OnInit, cloneStrategy(): void { const dialogRef = this.dialog.open(StrategyCloneComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { currentEnvironment: this.currentEnvironment, domainId: this.strategyList.parent.domainId @@ -252,7 +253,7 @@ export class StrategyDetailComponent extends DetailComponent implements OnInit, showChangeLog() { this.dialog.open(ChangeLogDialogComponent, { width: '1200px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { strategy: this.strategy, domainId: this.strategyList.parent.domainId, diff --git a/src/app/dashboard-module/domain-module/domain-detail/domain-detail.component.ts b/src/app/dashboard-module/domain-module/domain-detail/domain-detail.component.ts index 9d47699..4009a99 100644 --- a/src/app/dashboard-module/domain-module/domain-detail/domain-detail.component.ts +++ b/src/app/dashboard-module/domain-module/domain-detail/domain-detail.component.ts @@ -64,7 +64,7 @@ export class DomainDetailComponent extends DetailComponent implements OnInit, On ngOnInit() { this.loading = true; this.route.paramMap - .pipe(map(() => window.history.state)).pipe(takeUntil(this.unsubscribe)).subscribe(data => { + .pipe(map(() => globalThis.history.state)).pipe(takeUntil(this.unsubscribe)).subscribe(data => { if (data.element) { this.updateData(JSON.parse(data.element)); } else { @@ -111,19 +111,20 @@ export class DomainDetailComponent extends DetailComponent implements OnInit, On if (!this.editing) { this.classStatus = 'header editing'; this.editing = true; - } else { - this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; + return; + } - const newDescription = this.descElement.nativeElement.value; - if (super.validateEdition( - { description: this.domain.description }, - { description: newDescription})) { - this.editing = false; - return; - } + this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; - this.editDomain(newDescription); + const newDescription = this.descElement.nativeElement.value; + if (super.validateEdition( + { description: this.domain.description }, + { description: newDescription})) { + this.editing = false; + return; } + + this.editDomain(newDescription); } delete() { diff --git a/src/app/dashboard-module/domain-module/domain/domain.component.ts b/src/app/dashboard-module/domain-module/domain/domain.component.ts index 835e296..1cb2689 100644 --- a/src/app/dashboard-module/domain-module/domain/domain.component.ts +++ b/src/app/dashboard-module/domain-module/domain/domain.component.ts @@ -18,7 +18,7 @@ import { PathRoute, Types } from 'src/app/model/path-route'; import { Group } from 'src/app/model/group'; import { Config } from 'src/app/model/config'; import { Configuration, GraphQLConfigurationResultSet } from 'src/app/model/configuration'; -import { ApolloQueryResult } from '@apollo/client/core'; +import { Apollo } from 'apollo-angular/apollo'; import { AuthService } from 'src/app/auth/services/auth.service'; import { FeatureService } from 'src/app/services/feature.service'; import { BasicComponent } from '../common/basic-component'; @@ -69,7 +69,7 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy selectedConfigPath: string; currentPath = new PathRoute(); - prevScrollpos = window.scrollY; + prevScrollpos = globalThis.scrollY; navControl = false; slackIntegration = false; transferLabel = ''; @@ -115,7 +115,7 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy ngOnDestroy() { this.unsubscribe.next(); this.unsubscribe.complete(); - window.onscroll = () => { + globalThis.onscroll = () => { return; }; } @@ -123,7 +123,7 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy onDownloadSnapshot() { this.dialog.open(DomainSnapshotComponent, { width: '450px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { domainId: this.domainId } @@ -141,7 +141,7 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy this.transferLabel = 'Cancel Transfer'; this.dialog.open(DomainTransferDialogComponent, { width: '450px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { request_id: domain.id, domain: domain.name @@ -257,7 +257,7 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy this.currentPath = path; } - let query: Observable>; + let query: Observable>; if (this.currentPath.type === Types.GROUP_TYPE) { query = this.domainService.executeConfigurationGroupQuery(this.domainId, this.currentPath.id); } else if (this.currentPath.type === Types.CONFIG_TYPE) { @@ -305,9 +305,9 @@ export class DomainComponent extends BasicComponent implements OnInit, OnDestroy } private scrollMenuHandler() { - window.onscroll = () => { - if (!this.navControl && window.innerWidth < 1200) { - const currentScrollPos = window.scrollY; + globalThis.onscroll = () => { + if (!this.navControl && globalThis.innerWidth < 1200) { + const currentScrollPos = globalThis.scrollY; if (this.prevScrollpos > currentScrollPos) { document.getElementById("navbarMenu").style.top = "0"; } else { diff --git a/src/app/dashboard-module/domain-module/environments/environments.component.ts b/src/app/dashboard-module/domain-module/environments/environments.component.ts index f4b840c..7e93708 100644 --- a/src/app/dashboard-module/domain-module/environments/environments.component.ts +++ b/src/app/dashboard-module/domain-module/environments/environments.component.ts @@ -67,7 +67,7 @@ export class EnvironmentsComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -120,14 +120,14 @@ export class EnvironmentsComponent implements OnInit, OnDestroy { modalConfirmation.componentInstance.question = `Are you sure you want to remove ${selectedEnvironment.name}?`; modalConfirmation.result.then((result) => { if (result) { - const environment = this.environments.filter(env => env.id === selectedEnvironment.id); + const environment = this.environments.find(env => env.id === selectedEnvironment.id); this.envService.deleteEnvironment(selectedEnvironment.id) .pipe(takeUntil(this.unsubscribe)) .subscribe({ next: env => { if (env) { - this.environments.splice(this.environments.indexOf(environment[0]), 1); + this.environments.splice(this.environments.indexOf(environment), 1); this.toastService.showSuccess('Environment removed with success'); } }, @@ -188,7 +188,7 @@ export class EnvironmentsComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { if (data.length) { - data.forEach(element => { + for (const element of data) { if (element.action === 'UPDATE') { this.updatable = element.result === 'ok'; } else if (element.action === 'DELETE') { @@ -196,7 +196,7 @@ export class EnvironmentsComponent implements OnInit, OnDestroy { } else if (element.action === 'CREATE') { this.creatable = element.result === 'ok'; } - }); + } } }); } diff --git a/src/app/dashboard-module/domain-module/ext-gitops/ext-gitops.component.ts b/src/app/dashboard-module/domain-module/ext-gitops/ext-gitops.component.ts index 66cc948..c1aa25d 100644 --- a/src/app/dashboard-module/domain-module/ext-gitops/ext-gitops.component.ts +++ b/src/app/dashboard-module/domain-module/ext-gitops/ext-gitops.component.ts @@ -75,7 +75,7 @@ export class ExtGitOpsComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -96,7 +96,7 @@ export class ExtGitOpsComponent implements OnInit, OnDestroy { onStartNewAccount(): void { const dialogRef = this.dialog.open(GitOpsEnvSelectionComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { excludeEnvironments: this.gitOpsAccounts .filter(account => account.ID) @@ -161,7 +161,7 @@ export class ExtGitOpsComponent implements OnInit, OnDestroy { onUpdateTokens(): void { const dialogRef = this.dialog.open(GitOpsUpdateTokensComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { environments: this.gitOpsAccounts .filter(account => account.ID) @@ -334,13 +334,12 @@ export class ExtGitOpsComponent implements OnInit, OnDestroy { this.adminService.readCollabPermission(this.domainId, ['UPDATE'], 'ADMIN') .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { - data.forEach(perm => { + for (const perm of data) { if (perm.action === 'UPDATE') { this.allowUpdate = perm.result === 'ok'; } - }); - } - ); + } + }); } private updateRoute(): void { diff --git a/src/app/dashboard-module/domain-module/ext-slack/ext-slack.component.ts b/src/app/dashboard-module/domain-module/ext-slack/ext-slack.component.ts index 278538d..d7b33f7 100644 --- a/src/app/dashboard-module/domain-module/ext-slack/ext-slack.component.ts +++ b/src/app/dashboard-module/domain-module/ext-slack/ext-slack.component.ts @@ -59,7 +59,7 @@ export class ExtSlackComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -185,11 +185,11 @@ export class ExtSlackComponent implements OnInit, OnDestroy { this.adminService.readCollabPermission(this.domainId, ['UPDATE'], 'ADMIN') .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { - data.forEach(perm => { + for (const perm of data) { if (perm.action === 'UPDATE') { this.allowUpdate = perm.result === 'ok'; } - }); + } } ); } diff --git a/src/app/dashboard-module/domain-module/group/group-detail/group-detail.component.ts b/src/app/dashboard-module/domain-module/group/group-detail/group-detail.component.ts index a8230b0..7f9afd8 100644 --- a/src/app/dashboard-module/domain-module/group/group-detail/group-detail.component.ts +++ b/src/app/dashboard-module/domain-module/group/group-detail/group-detail.component.ts @@ -96,28 +96,29 @@ export class GroupDetailComponent extends DetailComponent implements OnInit, OnD if (!this.editing) { this.classStatus = 'header editing'; this.editing = true; - } else { - const { valid } = this.nameFormControl; + return; + } - if (valid) { - this.setBlockUI(true, 'Saving changes...'); - this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; + const { valid } = this.nameFormControl; - const body = { - name: this.nameElement.nativeElement.value, - description: this.descElement.nativeElement.value - }; + if (valid) { + this.setBlockUI(true, 'Saving changes...'); + this.classStatus = this.currentStatus ? 'header activated' : 'header deactivated'; - if (super.validateEdition( - { name: this.group.name, description: this.group.description }, - { name: body.name, description: body.description })) { - this.setBlockUI(false); - this.editing = false; - return; - } + const body = { + name: this.nameElement.nativeElement.value, + description: this.descElement.nativeElement.value + }; - this.editGroup(body); + if (super.validateEdition( + { name: this.group.name, description: this.group.description }, + { name: body.name, description: body.description })) { + this.setBlockUI(false); + this.editing = false; + return; } + + this.editGroup(body); } } @@ -207,8 +208,8 @@ export class GroupDetailComponent extends DetailComponent implements OnInit, OnD private editGroup(body: { name: string; description: string; }) { this.groupService.updateGroup(this.group.id, - body.name != this.group.name ? body.name : undefined, - body.description != this.group.description ? body.description : undefined) + body.name === this.group.name ? undefined : body.name, + body.description === this.group.description ? undefined : body.description) .pipe(takeUntil(this.unsubscribe)) .subscribe({ next: data => { diff --git a/src/app/dashboard-module/domain-module/group/group-list/group-list.component.ts b/src/app/dashboard-module/domain-module/group/group-list/group-list.component.ts index 74651d5..ecf3f88 100644 --- a/src/app/dashboard-module/domain-module/group/group-list/group-list.component.ts +++ b/src/app/dashboard-module/domain-module/group/group-list/group-list.component.ts @@ -81,7 +81,7 @@ export class GroupListComponent extends ListComponent implements OnInit, OnDestr createGroup(): void { const dialogRef = this.dialog.open(GroupCreateComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { name: '', description: '' } }); diff --git a/src/app/dashboard-module/domain-module/metric-module/metric-data/metric-data.component.ts b/src/app/dashboard-module/domain-module/metric-module/metric-data/metric-data.component.ts index e9011b7..2708123 100644 --- a/src/app/dashboard-module/domain-module/metric-module/metric-data/metric-data.component.ts +++ b/src/app/dashboard-module/domain-module/metric-module/metric-data/metric-data.component.ts @@ -60,11 +60,12 @@ export class MetricDataComponent implements OnInit, OnDestroy { this.readPermissionToObject(); this.pageLoaded = this.data.length; - this.totalPages = this.date ? this.parent.metrics.statistics.switchers + this.totalPages = this.date ? (() => { //if filtered by date, get the sum of positive and negative results - .filter(switcher => switcher.switcher === this.parent.switcher)[0].dateTimeStatistics - .filter(date => date.date == this.date) - .map(sumResults => sumResults.negative + sumResults.positive)[0] : + const switcherFound = this.parent.metrics.statistics.switchers.find(switcher => switcher.switcher === this.parent.switcher); + const dateStats = switcherFound?.dateTimeStatistics?.filter(date => date.date == this.date) || []; + return dateStats.map(sumResults => sumResults.negative + sumResults.positive)[0] || 0; + })() : //otherwise, just get the total from the statistics this.getTotalStatistics(); @@ -187,9 +188,12 @@ export class MetricDataComponent implements OnInit, OnDestroy { private getTotalStatistics(): number { if (this.parent.metrics.statistics.switchers.length) { - return this.parent.metrics.statistics.switchers - .filter(switcher => switcher.switcher === this.parent.switcher)[0].total; + const found = this.parent.metrics.statistics.switchers + .find(switcher => switcher.switcher === this.parent.switcher); + + return found ? found.total : 0; } + return 0; } @@ -217,16 +221,16 @@ export class MetricDataComponent implements OnInit, OnDestroy { } private customFilterPredicate(data: MetricData, filter: string): boolean { - return data.config.key.toLowerCase().indexOf(filter) >= 0 || - data.component.toLowerCase().indexOf(filter) >= 0 || - data.result && 'true'.indexOf(filter) >= 0 || - !data.result && 'false'.indexOf(filter) >= 0 || - data.date.toString().indexOf(filter) >= 0 || - data.reason.toLowerCase().toString().indexOf(filter) >= 0 || - data.message?.toLowerCase().toString().indexOf(filter) >= 0 || + return data.config.key.toLowerCase().includes(filter) || + data.component.toLowerCase().includes(filter) || + (data.result && 'true'.includes(filter)) || + (!data.result && 'false'.includes(filter)) || + data.date.toString().includes(filter) || + data.reason.toLowerCase().toString().includes(filter) || + data.message?.toLowerCase().includes(filter) || data.entry?.filter(e => { - return e.input.toLowerCase().toString().indexOf(filter) >= 0 || - e.strategy.toLowerCase().toString().indexOf(filter) >= 0; + return e.input.toLowerCase().toString().includes(filter) || + e.strategy.toLowerCase().toString().includes(filter); }).length > 0; } diff --git a/src/app/dashboard-module/domain-module/metric-module/metric-statistics/metric-statistics.component.ts b/src/app/dashboard-module/domain-module/metric-module/metric-statistics/metric-statistics.component.ts index 4d675fb..1e53469 100644 --- a/src/app/dashboard-module/domain-module/metric-module/metric-statistics/metric-statistics.component.ts +++ b/src/app/dashboard-module/domain-module/metric-module/metric-statistics/metric-statistics.component.ts @@ -130,14 +130,13 @@ export class SwitchersStatisticsTab { const negative = { ...negativeTemplate, data: [] }; const positive = { ...positiveTemplate, data: [] }; - switcherStatistics.forEach(switcherStats => { + for (const switcherStats of switcherStatistics) { this.barChartLabels.push(switcherStats.switcher); negative.data.push(switcherStats.negative); positive.data.push(switcherStats.positive); - }); + } - this.barChartData.push(negative); - this.barChartData.push(positive); + this.barChartData.push(negative, positive); } } @@ -171,14 +170,13 @@ export class ComponentsStatisticsTab { const negative = { ...negativeTemplate, data: [] }; const positive = { ...positiveTemplate, data: [] }; - componentsStatistics.forEach(componentStats => { + for (const componentStats of componentsStatistics) { this.barChartLabels.push(componentStats.component); negative.data.push(componentStats.negative); positive.data.push(componentStats.positive); - }); + } - this.barChartData.push(negative); - this.barChartData.push(positive); + this.barChartData.push(negative, positive); } } @@ -216,22 +214,18 @@ export class ReasonsStatisticsTab { datasets: [] }; - reasonsStatistics.forEach(reasonStats => { - this.chartData.datasets.push({ - data: [reasonStats.total], - label: reasonStats.reason + for (const reasonStats of reasonsStatistics) { + this.chartData.datasets.push({ + data: [reasonStats.total], + label: reasonStats.reason }); - }); + } } } export class SwitcherDateTimeGroupedTab { - constructor(private readonly parent: MetricStatisticsComponent) { - this.MAX_CONTENT = 5; - this.content_index = -1; - this.total_content = 0; - } + private readonly MAX_CONTENT = 5; public chartType: ChartType = 'line'; public chartLegend = true; @@ -250,10 +244,14 @@ export class SwitcherDateTimeGroupedTab { } }; - private readonly MAX_CONTENT: number; private content_index: number; private total_content: number; + constructor(private readonly parent: MetricStatisticsComponent) { + this.content_index = -1; + this.total_content = 0; + } + public loadSwitcherDateTimeGroupView(): void { this.chartDatasets = []; this.selectedData = []; @@ -264,7 +262,7 @@ export class SwitcherDateTimeGroupedTab { const negative = { ...negativeTemplate, data: [] }; const positive = { ...positiveTemplate, data: [] }; - switcherStatistics.forEach(switcherStats => { + for (const switcherStats of switcherStatistics) { this.total_content = switcherStats.dateTimeStatistics.length; const stats = switcherStats.dateTimeStatistics; @@ -280,10 +278,9 @@ export class SwitcherDateTimeGroupedTab { break; } - }); + } - this.chartDatasets.push(negative); - this.chartDatasets.push(positive); + this.chartDatasets.push(negative, positive); } hasNext(): boolean { @@ -308,7 +305,7 @@ export class SwitcherDateTimeGroupedTab { } pushMetric(swither: string, date: string) { - const dataFound = this.parent.data.data.filter(data => data.config.key === swither && data.date.toString().indexOf(date) >= 0); + const dataFound = this.parent.data.data.filter(data => data.config.key === swither && data.date.toString().includes(date)); this.selectedData.push(dataFound); } @@ -318,7 +315,7 @@ export class SwitcherDateTimeGroupedTab { if (metrics) { this.parent.dialog.open(SwitcherDataStatsDialogComponent, { width: '1200px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { stats: metrics.data, switcher: this.parent.switcher, @@ -335,7 +332,7 @@ export class SwitcherDateTimeGroupedTab { } showLabel(): boolean { - return window.screen.width > 750; + return globalThis.screen.width > 750; } } diff --git a/src/app/dashboard-module/domain-module/metric-module/metric/metric.component.ts b/src/app/dashboard-module/domain-module/metric-module/metric/metric.component.ts index 67747b7..392e348 100644 --- a/src/app/dashboard-module/domain-module/metric-module/metric/metric.component.ts +++ b/src/app/dashboard-module/domain-module/metric-module/metric/metric.component.ts @@ -66,7 +66,7 @@ export class MetricComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -95,7 +95,7 @@ export class MetricComponent implements OnInit, OnDestroy { const dialogRef = this.dialog.open(MetricFilterComponent, { width: '450px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { lockFilter: this.lockFilter, filter: key, diff --git a/src/app/dashboard-module/domain-module/team-module/team-detail/team-detail.component.ts b/src/app/dashboard-module/domain-module/team-module/team-detail/team-detail.component.ts index f1b710b..983ede0 100644 --- a/src/app/dashboard-module/domain-module/team-module/team-detail/team-detail.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team-detail/team-detail.component.ts @@ -71,7 +71,7 @@ export class TeamDetailComponent extends DetailComponent implements OnInit, OnDe ngOnInit() { this.loading = true; this.readPermissionToObject(); - this.activatedRoute.paramMap.pipe(map(() => window.history.state)) + this.activatedRoute.paramMap.pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { this.updateRoute(data.navigationId === 1); @@ -96,27 +96,28 @@ export class TeamDetailComponent extends DetailComponent implements OnInit, OnDe if (!this.editing) { this.editing = true; this.setHeaderStyle(); - } else { - const { valid } = this.nameFormControl; - - if (super.validateEdition( - { name: this.team.name }, - { name: this.nameFormControl.value })) { - this.editing = false; - this.setHeaderStyle(); - return; - } - - if (valid) { - this.editing = false; - this.setBlockUI(true, 'Updating Team...'); - this.teamService.updateTeam(this.team._id, this.nameFormControl.value, this.team.active ? 'true' : 'false') - .pipe(takeUntil(this.unsubscribe)) - .subscribe({ - next: team => this.onSuccess(team), - error: error => this.onError(error, `Unable to update team: '${this.team.name}'`) - }); - } + return; + } + + const { valid } = this.nameFormControl; + + if (super.validateEdition( + { name: this.team.name }, + { name: this.nameFormControl.value })) { + this.editing = false; + this.setHeaderStyle(); + return; + } + + if (valid) { + this.editing = false; + this.setBlockUI(true, 'Updating Team...'); + this.teamService.updateTeam(this.team._id, this.nameFormControl.value, this.team.active ? 'true' : 'false') + .pipe(takeUntil(this.unsubscribe)) + .subscribe({ + next: team => this.onSuccess(team), + error: error => this.onError(error, `Unable to update team: '${this.team.name}'`) + }); } } diff --git a/src/app/dashboard-module/domain-module/team-module/team-members/team-members.component.ts b/src/app/dashboard-module/domain-module/team-module/team-members/team-members.component.ts index 67baea8..09cf1e8 100644 --- a/src/app/dashboard-module/domain-module/team-module/team-members/team-members.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team-members/team-members.component.ts @@ -117,7 +117,7 @@ export class TeamMembersComponent implements OnInit, OnDestroy { this.inputMember.nativeElement.value = ''; this.dialog.open(TeamInviteDialogComponent, { width: '450px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { request_id: teamInvite._id, email: teamInvite.email, diff --git a/src/app/dashboard-module/domain-module/team-module/team-pending-members/team-pending-members.component.ts b/src/app/dashboard-module/domain-module/team-module/team-pending-members/team-pending-members.component.ts index 65226bd..0638f86 100644 --- a/src/app/dashboard-module/domain-module/team-module/team-pending-members/team-pending-members.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team-pending-members/team-pending-members.component.ts @@ -74,7 +74,7 @@ export class TeamPendingMembersComponent implements OnInit, OnDestroy { onGetInviteRequest(invite: any): void { this.dialog.open(TeamInviteDialogComponent, { width: '450px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { request_id: invite._id, email: invite.email, diff --git a/src/app/dashboard-module/domain-module/team-module/team-permissions/team-permissions.component.ts b/src/app/dashboard-module/domain-module/team-module/team-permissions/team-permissions.component.ts index 8a8dd39..7afe80e 100644 --- a/src/app/dashboard-module/domain-module/team-module/team-permissions/team-permissions.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team-permissions/team-permissions.component.ts @@ -69,10 +69,10 @@ export class TeamPermissionsComponent extends BasicComponent implements OnInit, } editPermission(permission: Permission) { - const permissionCopy = JSON.parse(JSON.stringify(permission)); + const permissionCopy = structuredClone(permission); const dialogRef = this.dialog.open(TeamPermissionCreateComponent, { width: '400px', - minWidth: window.innerWidth < 450 ? '95vw' : '', + minWidth: globalThis.innerWidth < 450 ? '95vw' : '', data: { domain: this.team.domain, permissions: this.dataSource.data, @@ -173,7 +173,7 @@ export class TeamPermissionsComponent extends BasicComponent implements OnInit, } formatContent(value: string): string { - if (window.screen.width < 560) + if (globalThis.screen.width < 560) return value.substring(0, 3); else return value; diff --git a/src/app/dashboard-module/domain-module/team-module/team-preview/team-preview.component.ts b/src/app/dashboard-module/domain-module/team-module/team-preview/team-preview.component.ts index 7c90052..1f240a2 100644 --- a/src/app/dashboard-module/domain-module/team-module/team-preview/team-preview.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team-preview/team-preview.component.ts @@ -97,26 +97,27 @@ export class TeamPreviewComponent extends BasicComponent implements OnInit, OnDe edit() { if (!this.editing) { this.editing = true; - } else { - const { valid } = this.nameFormControl; - - if (this.validateEdition( - { name: this.team.name }, - { name: this.nameFormControl.value })) { - this.editing = false; - return; - } + return; + } + + const { valid } = this.nameFormControl; + + if (this.validateEdition( + { name: this.team.name }, + { name: this.nameFormControl.value })) { + this.editing = false; + return; + } - if (valid) { - this.editing = false; - this.setBlockUI(true, 'Updating team...'); - this.teamService.updateTeam(this.team._id, this.nameFormControl.value, this.team.active ? 'true' : 'false') - .pipe(takeUntil(this.unsubscribe)) - .subscribe({ - next: team => this.onSuccess(team), - error: error => this.onError(error, `Unable to update team: '${this.team.name}'`) - }); - } + if (valid) { + this.editing = false; + this.setBlockUI(true, 'Updating team...'); + this.teamService.updateTeam(this.team._id, this.nameFormControl.value, this.team.active ? 'true' : 'false') + .pipe(takeUntil(this.unsubscribe)) + .subscribe({ + next: team => this.onSuccess(team), + error: error => this.onError(error, `Unable to update team: '${this.team.name}'`) + }); } } diff --git a/src/app/dashboard-module/domain-module/team-module/team/team.component.ts b/src/app/dashboard-module/domain-module/team-module/team/team.component.ts index 0f68a22..b31a40a 100644 --- a/src/app/dashboard-module/domain-module/team-module/team/team.component.ts +++ b/src/app/dashboard-module/domain-module/team-module/team/team.component.ts @@ -62,7 +62,7 @@ export class TeamComponent implements OnInit, OnDestroy { }); this.activatedRoute.paramMap - .pipe(map(() => window.history.state)) + .pipe(map(() => globalThis.history.state)) .pipe(takeUntil(this.unsubscribe)) .subscribe(data => this.fetch = data.navigationId === 1); } @@ -142,7 +142,7 @@ export class TeamComponent implements OnInit, OnDestroy { .pipe(takeUntil(this.unsubscribe)) .subscribe(data => { if (data.length) { - data.forEach(element => { + for (const element of data) { if (element.action === 'UPDATE') { this.updatable = element.result === 'ok'; } else if (element.action === 'DELETE') { @@ -152,7 +152,7 @@ export class TeamComponent implements OnInit, OnDestroy { } else if (element.action === 'READ') { this.readable = element.result === 'ok'; } - }); + } } }); } diff --git a/src/app/documentation-module/docs/markdown-injector.component.ts b/src/app/documentation-module/docs/markdown-injector.component.ts index 2392bce..f73cc02 100644 --- a/src/app/documentation-module/docs/markdown-injector.component.ts +++ b/src/app/documentation-module/docs/markdown-injector.component.ts @@ -11,11 +11,10 @@ export abstract class MarkdownInjector { protected init(documentToRender: string): void { this.markdownService.getSource(`${environment.docsUrl}${documentToRender}`).subscribe(data => { const darkPrefix = document.documentElement.classList.contains("dark-mode") ? '_dark' : ''; - - if (data) - this.markdown = data.replace(/\[\$ASSETS_LOCATION\]/g, environment.docsUrl).replace(/\[\$DARK_SUFFIX\]/g, darkPrefix); - else - this.markdown = `${environment.docsUrl}${documentToRender} not found`; + + this.markdown = data ? + data.replaceAll('[$ASSETS_LOCATION]', environment.docsUrl).replaceAll('[$DARK_SUFFIX]', darkPrefix) : + `${environment.docsUrl}${documentToRender} not found`; }); document.documentElement.addEventListener('dark-mode', () => { diff --git a/src/app/documentation-module/search-item/search-item.component.ts b/src/app/documentation-module/search-item/search-item.component.ts index 92bc32e..4394f53 100644 --- a/src/app/documentation-module/search-item/search-item.component.ts +++ b/src/app/documentation-module/search-item/search-item.component.ts @@ -30,10 +30,11 @@ export class SearchItemComponent { } renderSegment(data: string, documentToRender: string) { - if (data) - return data.replace(/\[\$ASSETS_LOCATION\]/g, environment.docsUrl); - else - return `${environment.docsUrl}${documentToRender} not found`; + if (data) { + return data.replaceAll('[$ASSETS_LOCATION]', environment.docsUrl); + } + + return `${environment.docsUrl}${documentToRender} not found`; } } diff --git a/src/app/login/login.component.ts b/src/app/login/login.component.ts index de77516..ce84089 100644 --- a/src/app/login/login.component.ts +++ b/src/app/login/login.component.ts @@ -84,17 +84,17 @@ export class LoginComponent implements OnInit, OnDestroy { onGitHubLogin() { this.loading = true; - window.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; + globalThis.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; } onBitbucketLogin() { this.loading = true; - window.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; + globalThis.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; } onSamlLogin() { this.loading = true; - window.location.href = `${environment.apiUrl}/admin/saml/login`; + globalThis.location.href = `${environment.apiUrl}/admin/saml/login`; } ngOnDestroy() { @@ -158,8 +158,8 @@ export class LoginComponent implements OnInit, OnDestroy { } private loginWithSAMLToken() { - if (window.location.hash) { - const hashParams = new URLSearchParams(window.location.hash.substring(1)); + if (globalThis.location.hash) { + const hashParams = new URLSearchParams(globalThis.location.hash.substring(1)); const token = hashParams.get('token'); if (token) { diff --git a/src/app/services/pwa.service.ts b/src/app/services/pwa.service.ts index c273086..1f9bb3a 100644 --- a/src/app/services/pwa.service.ts +++ b/src/app/services/pwa.service.ts @@ -21,7 +21,7 @@ export class PwaService { ) .subscribe(() => { const snack = this.snackbar.open('Update Available', 'Reload'); - snack.onAction().subscribe(() => window.location.reload()); + snack.onAction().subscribe(() => globalThis.location.reload()); }); } } diff --git a/src/app/services/strategy.service.ts b/src/app/services/strategy.service.ts index f3ea597..86e2b78 100644 --- a/src/app/services/strategy.service.ts +++ b/src/app/services/strategy.service.ts @@ -33,22 +33,23 @@ export class StrategyService extends ApiService { public getAvailableOperations(strategy: Strategy, requirements: StrategyReq): string[] { const operations = []; - requirements.operationRequirements.forEach(opReq => { + for (const opReq of requirements.operationRequirements) { if (opReq.max >= strategy.values.length) { operations.push(opReq.operation); - } - }) + } + } return operations; } public validateStrategy(operation: string, values: string[], requirements: StrategyReq): boolean { - const operationReq = requirements.operationRequirements.filter(op => op.operation === operation)[0]; + const operationReq = requirements.operationRequirements.find(op => op.operation === operation); - if (values.length > operationReq.max || values.length < operationReq.min) + if (!operationReq) { return false; - else - return true; + } + + return values.length <= operationReq.max && values.length >= operationReq.min; } public setStrategyEnvironmentStatus(id: string, env: string, status: boolean): Observable { diff --git a/src/app/settings-module/settings-account/settings-account.component.ts b/src/app/settings-module/settings-account/settings-account.component.ts index 4c896b8..aa55b70 100644 --- a/src/app/settings-module/settings-account/settings-account.component.ts +++ b/src/app/settings-module/settings-account/settings-account.component.ts @@ -113,10 +113,10 @@ export class SettingsAccountComponent extends BasicComponent implements OnInit, getPlatformIcon(): string { switch (this.userPlatform) { - case 'Bitbucket': return "assets\\bitbucket.svg"; - case 'GitHub': return "assets\\github.svg"; - case 'SAML': return "assets\\saml.svg"; - default: return "assets\\switcherapi_mark_grey.png"; + case 'Bitbucket': return String.raw`assets\bitbucket.svg`; + case 'GitHub': return String.raw`assets\github.svg`; + case 'SAML': return String.raw`assets\saml.svg`; + default: return String.raw`assets\switcherapi_mark_grey.png`; } } @@ -130,7 +130,7 @@ export class SettingsAccountComponent extends BasicComponent implements OnInit, this.userPlatform = this.authService.getUserInfo('platform'); const avatar = this.authService.getUserInfo('avatar'); - this.profileAvatar = avatar || "assets\\switcherapi_mark_icon.png"; + this.profileAvatar = avatar || String.raw`assets\switcherapi_mark_icon.png`; } private loadDomains(): void { diff --git a/src/app/signup/signup.component.ts b/src/app/signup/signup.component.ts index 5e50747..161bceb 100644 --- a/src/app/signup/signup.component.ts +++ b/src/app/signup/signup.component.ts @@ -87,17 +87,17 @@ export class SignupComponent implements OnInit, OnDestroy { onGitHubLogin() { this.loading = true; - window.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; + globalThis.location.href = `https://github.com/login/oauth/authorize?client_id=${environment.githubApiClientId}`; } onBitbucketLogin() { this.loading = true; - window.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; + globalThis.location.href = `https://bitbucket.org/site/oauth2/authorize?client_id=${environment.bitbucketApiClientId}&response_type=code`; } onSamlLogin() { this.loading = true; - window.location.href = `${environment.apiUrl}/admin/saml/login`; + globalThis.location.href = `${environment.apiUrl}/admin/saml/login`; } ngOnDestroy() { diff --git a/src/assets/documentation/api.md b/src/assets/documentation/api.md index 9f620ed..7ead01d 100644 --- a/src/assets/documentation/api.md +++ b/src/assets/documentation/api.md @@ -170,5 +170,5 @@ A GraphQL endpoint can also be used to execute the API. Extra return information *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/components.md b/src/assets/documentation/components.md index 1a6f6f3..f8d3c47 100644 --- a/src/assets/documentation/components.md +++ b/src/assets/documentation/components.md @@ -7,7 +7,7 @@ Components are registered applications that will be making use of switchers. Regarding security, all registered applications must be linked to a switcher, otherwise, the call will return access denied.
This gateway has been created to prevent other applications to use not related switchers. - +

@@ -22,7 +22,7 @@ Creating a components is very simple and after doing it, you just have to link i Enter in the edition mode and just type the name of your registered component.
It will try to match in an auto-complete list for you to choose afterwards. - +

@@ -47,5 +47,5 @@ Creating a components is very simple and after doing it, you just have to link i *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/environment.md b/src/assets/documentation/environment.md index 447b915..e2eb547 100644 --- a/src/assets/documentation/environment.md +++ b/src/assets/documentation/environment.md @@ -7,7 +7,7 @@ You can benefit from working with different environments when using Switcher API - Decrease the impact of your either productive or other sensitive environments when switching features. - Set up exclusive variables (strategies) that will be used to run the switcher criteria. - +

@@ -21,7 +21,7 @@ After registering a new environment, it will become available as an option for: - Strategies - Snapshot download (for zero-latency library usage) - + **default** is your productive environment @@ -30,11 +30,11 @@ Environment settings can be also be reset using two different options. 1. Removing the environment setting inside your Domain, Group, Switcher, or Strategy, by selecting the trach icon. In case your application uses the removed environment, then it will automatically use the default environment settings. -

+

2. Resetting the entire environment to use the default settings. This action will also remove strategies since they are created per environment. -

+



@@ -43,7 +43,7 @@ In case your application uses the removed environment, then it will automaticall When using the provided modules to connect to the Switcher API, it is also possible to opt for accessing all configuration made inside Switcher Management via a snapshot configuration file. This file is generated in JSON and can be download by selecting the icon on the top of your Domain panel. -

+

Snapshots can be downloaded by selecting the environment and one of the optional configurations.
It is also possible to configure the module to auto-update this snapshot locally. This setting is useful when working with critical services that cannot depend on network latency. @@ -75,5 +75,5 @@ When using the provided modules to connect to the Switcher API, it is also possi *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/gitops.md b/src/assets/documentation/gitops.md index 6450bce..137b7a9 100644 --- a/src/assets/documentation/gitops.md +++ b/src/assets/documentation/gitops.md @@ -8,7 +8,7 @@ - Flexible settings allow you to define the best workflow for your organization - Orchestrates accounts per Domain environments allowing seamless integration with any branching strategy -

+

#### Getting Started * * * @@ -114,5 +114,5 @@ No, the token is encrypted and stored securely. *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/libjava.md b/src/assets/documentation/libjava.md index 71ada11..a8ac175 100644 --- a/src/assets/documentation/libjava.md +++ b/src/assets/documentation/libjava.md @@ -7,20 +7,26 @@ A Java SDK for Switcher API *** -### Features -- Flexible and robust SDK that will keep your code clean and maintainable. -- Able to work local using a snapshot file pulled from your remote Switcher-API Domain. -- Silent mode is a hybrid configuration that automatically enables contingent sub-processes in case of any connectivity issue. -- Built-in test annotation for clear and easy implementation of automated testing. -- Easy to set up. Switcher Context is responsible to manage all the configuration complexity between your application and API. +### Overview -* * * +The Switcher Client SDK is a comprehensive Java library for integrating with [Switcher API](https://github.com/switcherapi/switcher-api), a cloud-based feature flag management system. This SDK enables you to control feature toggles, A/B testing, and configuration management in your Java applications. + +### Key Features + +- **๐Ÿ”ง Flexible Configuration**: Multiple configuration approaches to fit different application architectures +- **๐Ÿ“ Local & Remote Operation**: Works with remote API or local snapshot files for offline capability +- **๐Ÿ”„ Real-time Updates**: Hot-swapping support with automatic snapshot synchronization +- **๐Ÿงช Testing Support**: Built-in testing utilities and annotations for automated testing +- **โšก Performance Optimized**: Includes throttling, caching, and async execution capabilities +- **๐Ÿ›ก๏ธ Resilient**: Silent mode with automatic fallback mechanisms for high availability +- **๐Ÿ” Easy Debugging**: Comprehensive execution history and metadata tracking + +### Installation -### Usage +#### Maven Dependency + +Add the following dependency to your `pom.xml`: -##### - Install -- Using the source code `mvn clean install` -- Adding as a dependency - Maven ```xml com.switcherapi @@ -29,255 +35,331 @@ A Java SDK for Switcher API ``` -- Gradle +#### Build from Source +```bash +mvn clean install ``` -implementation 'com.switcherapi:switcher-client:[VERSION]' -``` - -###### Compatibility with Jakarta EE 9 -Use SDK v1.x for applications not using Jakarta EE 9.
-Use SDK v2.x for Jakarta EE 9 based applications. - -
-##### - Client Context Properties -Define a feature class that extends SwitcherContext. This implementation will centralize all features in a single place of your application and will have all the operations and features available to access the API either remotely or locally from the snapshot files. +#### Version Compatibility -The Client SDK configuration must be defined in a properties file that contains all parameters for your application to start communicating with the API. +| SDK Version | Java Version | Jakarta EE | Description | +|-------------|--------------|------------|-------------| +| v1.x | Java 8+ | No | For traditional Java EE applications | +| v2.x | Java 17+ | Yes | For Jakarta EE 9+ applications | -1. Inside the resources' folder, create a file named: switcherapi.properties. +### Quick Start -Configure the parameters according to the definition below.
-You can also use environment variables using the standard notation ${VALUE:DEFAULT_VALUE} +Here's a minimal example to get you started: -**Required** - -```properties -# Path of the feature class that extends SwitcherContext -switcher.context - -# Swither-API endpoint (default value uses Switcher API cloud endpoint) -switcher.url - -# Switcher-API key generated for the application/component -switcher.apikey - -# Application/component name -switcher.component +```java +// 1. Define your feature flags +public class MyAppFeatures extends SwitcherContext { + @SwitcherKey + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + + @SwitcherKey + public static final String FEATURE_PREMIUM = "FEATURE_PREMIUM"; +} -# Domain name -switcher.domain +// 2. Use in your application +public class MyService { + public void processRequest() { + if (MyAppFeatures.getSwitcher(FEATURE_NEW_UI).isItOn()) { + // Use new UI logic + } else { + // Use legacy UI logic + } + } +} ``` -**Optional** - -```properties -# Environment name. Production environment is named as 'default' -switcher.environment - -# true/false When local, it will only use a local snapshot -switcher.local - -# true/false When true, it will check Switcher Keys -switcher.check +### Configuration -# Folder from where snapshots will be saved/read -switcher.snapshot.location +#### Using SwitcherContext (Properties-based) -# true/false Automated lookup for snapshot when initializing the client -switcher.snapshot.auto +This approach automatically loads configuration from a properties file, ideal for applications with externalized configuration. -# true/false Enable the watcher to monitor the snapshot file for changes during runtime -switcher.snapshot.watcher +##### Step 1: Create Configuration File -# true/false Skip snapshotValidation() that can be used for UT executions -switcher.snapshot.skipvalidation +Create `src/main/resources/switcherapi.properties`: -# Enable the Snapshot Auto Update given an interval of time - e.g. 1s (s: seconds, m: minutes) -switcher.snapshot.updateinterval - -# Enable contigency given the time for the client to retry - e.g. 5s (s: seconds - m: minutes - h: hours) -switcher.silent +```properties +### Required Configuration +switcher.context=com.example.MyAppFeatures +switcher.url=https://api.switcherapi.com +switcher.apikey=YOUR_API_KEY +switcher.component=my-application +switcher.domain=MY_DOMAIN + +### Optional Configuration +switcher.environment=default +switcher.timeout=3000 +switcher.poolsize=2 +``` -# Path to the truststore file -switcher.truststore.path -> Path to the truststore file +##### Configuration Properties Reference + +| Property | Required | Default | Description | +|-------------------------------------|----------|---------|--------------------------------------------------------------------------------------| +| `switcher.context` | โœ… | - | Fully qualified class name extending SwitcherContext | +| `switcher.url` | โœ… | - | Switcher API endpoint URL | +| `switcher.apikey` | โœ… | - | API key for authentication | +| `switcher.component` | โœ… | - | Your application/component identifier | +| `switcher.domain` | โœ… | - | Domain name in Switcher API | +| `switcher.environment` | โŒ | default | Environment name (dev, staging, default) | +| `switcher.local` | โŒ | false | Enable local-only mode | +| `switcher.check` | โŒ | false | Validate switcher keys on startup | +| `switcher.relay.restrict` | โŒ | true | Defines if client will trigger local snapshot relay verification | +| `switcher.snapshot.location` | โŒ | - | Directory for snapshot files | +| `switcher.snapshot.auto` | โŒ | false | Auto-load snapshots on startup | +| `switcher.snapshot.skipvalidation` | โŒ | false | Skip snapshot validation on load | +| `switcher.snapshot.updateinterval` | โŒ | - | Interval for automatic snapshot updates (e.g., "5s", "2m") | +| `switcher.snapshot.watcher` | โŒ | false | Monitor snapshot files for changes | +| `switcher.silent` | โŒ | - | Enable silent mode (e.g., "5s", "2m") | +| `switcher.timeout` | โŒ | 3000 | API timeout in milliseconds | +| `switcher.poolsize` | โŒ | 2 | Thread pool size for API calls | +| `switcher.regextimeout` (v1-only) | โŒ | 3000 | Time in ms given to Timed Match Worker used for local Regex (ReDoS safety mechanism) | +| `switcher.truststore.path` | โŒ | - | Path to custom truststore file | +| `switcher.truststore.password` | โŒ | - | Password for custom truststore | + +> ๐Ÿ’ก **Environment Variables**: Use `${ENV_VAR:default_value}` syntax for environment variable substitution. + +##### Step 2: Define Feature Class -# Truststore password -switcher.truststore.password -> Truststore password +```java +public class MyAppFeatures extends SwitcherContext { + @SwitcherKey + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + + @SwitcherKey + public static final String FEATURE_PREMIUM = "FEATURE_PREMIUM"; +} +``` -# Time in ms given to the API to respond - 3000 default value -switcher.timeout -> Time in ms given to the API to respond - 3000 default value -switcher.poolsize -> Number of threads used to execute the API - 2 default value +#### Using SwitcherContextBase (Programmatic) -(Java 8 applications only) -switcher.regextimeout -> Time in ms given to Timed Match Worker used for local Regex (ReDoS safety mechanism) - 3000 default value -``` +This approach provides more flexibility and is ideal for applications requiring dynamic configuration. -The Base Context provides with a more flexible way to configure the Client SDK.
-Instead of using SwitcherContext, which is used to automatically load from the switcherapi.properties, you can also use SwitcherContextBase and supply the ContextBuilder to include the settings. +##### Basic Programmatic Configuration ```java -MyAppFeatures.configure(ContextBuilder.builder() - .contextLocation("com.switcherapi.playground.Features") - .apiKey("API_KEY") - .url("https://api.switcherapi.com") - .domain("Playground") - .component("switcher-playground")); - -MyAppFeatures.initializeClient(); +public class MyAppFeatures extends SwitcherContextBase { + @SwitcherKey + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + + // Configure programmatically + static { + configure(ContextBuilder.builder() + .context(MyAppFeatures.class.getName()) + .apiKey("YOUR_API_KEY") + .url("https://api.switcherapi.com") + .domain("MY_DOMAIN") + .component("my-application") + .environment("default")); + + initializeClient(); + } +} ``` -Or simply define a custom file properties to load everything from it. +##### Spring Boot Integration ```java -// Load from resources/switcherapi-test.properties -MyAppFeatures.loadProperties("switcherapi-test"); +@ConfigurationProperties(prefix = "switcher") +public class MySwitcherConfig extends SwitcherContextBase { + + @SwitcherKey + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + + @Override + @PostConstruct + public void configureClient() { + // Add any pre-configuration logic here + super.configureClient(); + // Add any post-configuration logic here + } +} ``` -Or using configureClient() with @PostConstruct to handle all the configuration build boilerplate. +##### Custom Properties File ```java -@ConfigurationProperties -class MySwitcherClientConfig extends SwitcherContextBase { - - @SwitcherKey - public static final String MY_SWITCHER = "MY_SWITCHER"; - - @Override - @PostConstruct - public void configureClient() { - // you can add pre-configuration here - super.configureClient(); - // you can add post-configuration here - } -} +// Load from custom properties file +MyAppFeatures.loadProperties("switcherapi-test"); ``` -2. Defining your features +#### Defining Feature Flags -Create a class that extends SwitcherContext if you are loading the configuration from the switcherapi.properties file. -Or use SwitcherContextBase to define the configuration using the ContextBuilder or SwitcherConfig. +Feature flags must follow specific conventions for proper functionality: ```java public class MyAppFeatures extends SwitcherContext { - - @SwitcherKey - public static final String MY_SWITCHER = "MY_SWITCHER"; - + + // โœ… Correct: public static final String + @SwitcherKey + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + + @SwitcherKey + public static final String FEATURE_PREMIUM_ACCESS = "FEATURE_PREMIUM_ACCESS"; + + // โŒ Incorrect examples: + // private static final String WRONG = "WRONG"; // Not public + // public static String WRONG2 = "WRONG2"; // Not final + // public final String WRONG3 = "WRONG3"; // Not static } - -Switcher mySwitcher = MyAppFeatures.getSwitcher(MY_SWITCHER); -mySwitcher.isItOn(); ``` -##### - Executing -There are a few different ways to call the API using the java library. -
Here are some examples: +**Why these conventions matter:** +- **`public`**: Accessible from other parts of your application +- **`static`**: No need to instantiate the class to access the constant +- **`final`**: Prevents accidental modification during runtime -1. **No parameters** +You can also name your feature flag attributes differently, but ensure the values match those defined in Switcher API. -Invoking the API can be done by obtaining the switcher object and calling *isItOn*. +### Usage Patterns + +#### 1. Basic Flag Checking ```java -Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01); -switcher.isItOn(); +// Simple boolean check +Switcher switcher = MyAppFeatures.getSwitcher(FEATURE_NEW_UI); +if (switcher.isItOn()) { + // Feature is enabled +} ``` -Or, you can submit the switcher request and get the switcher result, which contains result, reason and metadata that can be used for any additional verification. +#### 2. Detailed Result Information ```java +Switcher switcher = MyAppFeatures.getSwitcher(FEATURE_NEW_UI); SwitcherResult result = switcher.submit(); -result.isItOn(); // true/false -result.getReason(); // Descriptive response based on result value -result.getMetadata(YourMetadata.class); // Additional information + +if (result.isItOn()) { + System.out.println("Feature enabled: " + result.getReason()); + // Access additional metadata if needed + MyMetadata metadata = result.getMetadata(MyMetadata.class); +} ``` -2. **Strategy validation - preparing input** +#### 3. Strategy Validation with Input Parameters -Loading information into the switcher can be made by using *prepareEntry*, in case you want to include input from a different place of your code. Otherwise, it is also possible to include everything in the same call. +##### Preparing Input Separately ```java List entries = new ArrayList<>(); -entries.add(Entry.of(StrategyValidator.DATE, "2019-12-10")); -entries.add(Entry.of(StrategyValidator.DATE, "2020-12-10")); +entries.add(Entry.of(StrategyValidator.DATE, "2024-01-01")); +entries.add(Entry.of(StrategyValidator.TIME, "14:00")); switcher.prepareEntry(entries); -switcher.isItOn(); +boolean isEnabled = switcher.isItOn(); ``` -3. **Strategy validation - Fluent style** - -Create chained calls to validate the switcher with a more readable and maintainable code. +##### Fluent API Style (Recommended) ```java -import static **.MyAppFeatures.*; +import static com.example.MyAppFeatures.*; -getSwitcher(FEATURE01) - .checkValue("My value") - .checkNetwork("10.0.0.1") - .isItOn(); +boolean isEnabled = getSwitcher(FEATURE_PREMIUM_ACCESS) + .checkValue("premium_user") + .checkNetwork("192.168.1.0/24") + .checkDate("2024-01-01") + .isItOn(); ``` -4. **Accessing the last SwitcherResult** +#### 4. Execution History Tracking + +```java +Switcher switcher = getSwitcher(FEATURE_NEW_UI) + .keepExecutions() // Enable execution tracking + .checkValue("user_type"); -Switchers stores the last execution result, which can be retrieved using the following operation. +switcher.isItOn(); + +// Access the last execution result +SwitcherResult lastResult = switcher.getLastExecutionResult(); +System.out.println("Last execution reason: " + lastResult.getReason()); + +// Clear history when needed +switcher.flushExecutions(); +``` + +#### 5. Performance Optimization with Throttling ```java -switcher.getLastExecutionResult(); +// Execute asynchronously with 1-second throttle +// Returns cached result if called within throttle period +boolean isEnabled = switcher.throttle(1000).isItOn(); ``` -5. **Throttling** +### Operating Modes + +#### Remote Mode -Run Switchers asynchronously when using throttling. It will return the last known value until the throttle time is over. +Default mode that communicates directly with Switcher API. ```java -switcher.throttle(1000).isItOn(); +MyAppFeatures.configure(ContextBuilder.builder() + .url("https://api.switcherapi.com") + .apiKey("YOUR_API_KEY") + .domain("MY_DOMAIN") + .component("my-app")); + +MyAppFeatures.initializeClient(); ``` -
+**Use Cases:** +- Real-time feature flag updates +- A/B testing with immediate changes +- Centralized configuration management -##### - Local settings -You can also set the Switcher library to work locally. It will use a local snapshot file to retrieve the switchers configuration. +#### Local Mode + +Uses local snapshot files without API communication. ```java MyAppFeatures.configure(ContextBuilder.builder() - .local(true) - .snapshotLocation("/src/resources")); + .local(true) + .snapshotLocation("./src/main/resources/snapshots")); MyAppFeatures.initializeClient(); - -Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01); -switcher.isItOn(); ``` -
-##### - Hybrid settings -Forcing Switchers to resolve remotely can help you define exclusive features that cannot be resolved locally.
-This feature is ideal if you want to run the SDK in local mode but still want to resolve a specific switcher remotely. +**Use Cases:** +- Offline environments +- High-performance scenarios where API latency is critical +- Development and testing environments + +#### Hybrid Mode + +Combines remote and local capabilities for optimal flexibility. + +##### Force Remote Resolution ```java +// Force specific switcher to resolve remotely even in local mode switcher.forceRemote().isItOn(); ``` -Another option is to use in-memory loaded snapshots to resolve the switchers.
-Switcher SDK will schedule a background task to update snapshot in-memory a new version is available. +##### In-Memory Snapshots with Auto-Update ```java MyAppFeatures.configure(ContextBuilder.builder() .url("https://api.switcherapi.com") - .apiKey("[API-KEY]") - .domain("Playground") + .apiKey("YOUR_API_KEY") + .domain("MY_DOMAIN") .local(true) .snapshotAutoLoad(true) - .snapshotAutoUpdateInterval("5s") // You can choose to configure here or using `scheduleSnapshotAutoUpdate` - .component("switcher-playground")); + .snapshotAutoUpdateInterval("30s") // Check for updates every 30 seconds + .component("my-app")); MyAppFeatures.initializeClient(); -MyAppFeatures.scheduleSnapshotAutoUpdate("5s", new SnapshotCallback() { + +// Optional: Schedule with callback for monitoring +MyAppFeatures.scheduleSnapshotAutoUpdate("30s", new SnapshotCallback() { @Override public void onSnapshotUpdate(long version) { - logger.info("Snapshot updated: {}", version); + logger.info("Snapshot updated to version: {}", version); } @Override @@ -286,154 +368,215 @@ MyAppFeatures.scheduleSnapshotAutoUpdate("5s", new SnapshotCallback() { } }); ``` -
-##### Real-time snapshot reload (Hot-swapping) -Let the Switcher Client manage your application local snapshot.
-These features allow you to configure the SDK to automatically update the snapshot during runtime. +### Advanced Features + +#### Real-time Snapshot Management + +##### File System Watcher + +Monitor snapshot files for external changes: -1. This feature will update the in-memory Snapshot every time the file is modified. ```java +// Start watching for file changes MyAppFeatures.watchSnapshot(); + +// Stop watching when no longer needed MyAppFeatures.stopWatchingSnapshot(); ``` -Alternatively, you can also set the Switcher Context configuration to start watching the snapshot file during the client initialization. +Or enable during initialization: ```java MyAppFeatures.configure(ContextBuilder.builder() - .snapshotWatcher(true)); - -MyAppFeatures.initializeClient(); + .snapshotWatcher(true) + .snapshotLocation("./src/main/resources/snapshots")); ``` -2. You can also perform snapshot update validation to verify if there are changes to be pulled. +##### Manual Snapshot Validation + ```java -MyAppFeatures.validateSnapshot(); +// Check if remote snapshot is newer than local +boolean hasUpdates = MyAppFeatures.validateSnapshot(); +if (hasUpdates) { + logger.info("New snapshot version available"); +} ``` -3. Enable the Client SDK to execute Snapshot Auto Updates in the background using configuration. It basically encapsulates the validateSnapshot feature into a scheduled task managed by the SDK. +##### Automated Background Updates ```java -// It will check and update the local/in-memory snapshot to the latest version every second MyAppFeatures.configure(ContextBuilder.builder() - .snapshotAutoUpdateInterval("1s") - .snapshotLocation("/src/resources")); + .snapshotAutoUpdateInterval("5m") // Check every 5 minutes + .snapshotLocation("./src/main/resources/snapshots")); ``` -
+#### Performance Optimization -##### - Built-in test feature -Write automated tests using this built-in test annotation to guide your test scenario according to what you want to test. -
*SwitcherExecutor* implementation has 2 methods that can make mock tests easier. Use assume to force a value to a switcher and forget to reset its original state. +##### Silent Mode (Resilience) -```java -Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01); +Automatically fall back to cached results when API is unavailable: -SwitcherBypass.assume(FEATURE01, false); -switcher.isItOn(); // 'false' - -SwitcherBypass.forget(FEATURE01); -switcher.isItOn(); // Now, it's going to return the result retrieved from the API or the Snapshot file +```java +MyAppFeatures.configure(ContextBuilder.builder() + .silentMode("30s") // Retry API calls every 30 seconds when failing + .url("https://api.switcherapi.com") + // ... other config +); ``` -For more complex scenarios where you need to test features based on specific inputs, you can use test conditions. +**Time formats supported:** +- `5s` - 5 seconds +- `2m` - 2 minutes +- `1h` - 1 hour -```java -Switcher switcher = MyAppFeatures.getSwitcher(FEATURE01).checkValue("My value"); - -SwitcherBypass.assume(FEATURE01, true).when(StrategyValidator.VALUE, "My value"); -switcher.isItOn(); // 'true' +##### Connection Pooling +```java +MyAppFeatures.configure(ContextBuilder.builder() + .timeoutMs(5000) // 5 second timeout + .poolConnectionSize(5) // 5 concurrent connections + // ... other config +); ``` -
+### Testing -##### - Smoke test -Validate Switcher Keys on your testing pipelines before deploying a change. -Switcher Keys may not be configured correctly and can cause your code to have undesired results. +#### Built-in Test Utilities -This feature will validate using the context provided to check if everything is up and running. -In case something is missing, this operation will throw an exception pointing out which Switcher Keys are not configured. +##### SwitcherBypass for Unit Tests ```java @Test -void testSwitchers() { - assertDoesNotThrow(() -> MyAppFeatures.checkSwitchers()); +void testFeatureEnabled() { + // Force switcher to return specific value + SwitcherBypass.assume(FEATURE_NEW_UI, true); + + assertTrue(myService.usesNewUI()); + + // Reset to original behavior + SwitcherBypass.forget(FEATURE_NEW_UI); +} + +@Test +void testWithConditions() { + Switcher switcher = MyAppFeatures.getSwitcher(FEATURE_PREMIUM_ACCESS) + .checkValue("user_type"); + + // Assume true only when specific condition is met + SwitcherBypass.assume(FEATURE_PREMIUM_ACCESS, true) + .when(StrategyValidator.VALUE, "premium"); + + assertTrue(switcher.isItOn()); } ``` -Alternatively, you can also set the Switcher Context configuration to check during the client initialization. +##### JUnit 5 Integration -```java -MyAppFeatures.configure(ContextBuilder.builder() - .checkSwitchers(true)); +###### Single Switcher Test -MyAppFeatures.initializeClient(); +```java +@SwitcherTest(key = FEATURE_NEW_UI, result = true) +void testNewUIFeature() { + // FEATURE_NEW_UI will return true during this test + assertTrue(myService.usesNewUI()); + // Automatically resets after test completion +} ``` -#### SwitcherTest annotation - Requires JUnit 5 Jupiter -Predefine Switchers result outside your test methods with the SwitcherTest annotation. -
It encapsulates the test and makes sure that the Switcher returns to its original state after concluding the test. +###### Multiple Switchers -Simple use case (result is default to true, so it can be omitted): ```java -@SwitcherTest(key = MY_SWITCHER, result = true) -void testMyFeature() { - assertTrue(instance.myFeature()); +@SwitcherTest(switchers = { + @SwitcherTestValue(key = FEATURE_NEW_UI, result = true), + @SwitcherTestValue(key = FEATURE_PREMIUM_ACCESS, result = false) +}) +void testMultipleFeatures() { + assertTrue(myService.usesNewUI()); + assertFalse(myService.hasPremiumAccess()); } ``` -Multiple Switchers where more than one Switcher is used in the test: +###### A/B Testing + ```java -@SwitcherTest(switchers = { - @SwitcherTestValue(key = MY_SWITCHER), - @SwitcherTestValue(key = MY_SWITCHER2) -}) -void testMyFeature() { - assertTrue(instance.myFeature()); +@SwitcherTest(key = FEATURE_NEW_UI, abTest = true) +void testFeatureABTesting() { + // Test passes regardless of switcher result + // Useful for testing both code paths + myService.handleUILogic(); } ``` -AB Test scenario where your test should return the same result regardless of the Switcher result: +###### Conditional Testing + ```java -@SwitcherTest(key = MY_SWITCHER, abTest = true) -void testMyFeature() { - assertTrue(instance.myFeature()); +@SwitcherTest( + key = FEATURE_PREMIUM_ACCESS, + result = true, + when = @SwitcherTestWhen(value = "premium_user") +) +void testPremiumFeature() { + // Test with specific input conditions + assertTrue(myService.checkPremiumAccess("premium_user")); } ``` -Using SwitcherTestWhen to define a specific condition for the test: +#### Smoke Testing + +Validate all switcher keys are properly configured: + ```java -@SwitcherTest(key = MY_SWITCHER, when = @SwitcherTestWhen(value = "My value")) -void testMyFeature() { - assertTrue(instance.myFeature()); +@Test +void validateSwitcherConfiguration() { + // Throws exception if any switcher key is not found + assertDoesNotThrow(() -> MyAppFeatures.checkSwitchers()); } ``` -#### Native Image -Switcher Client is fully compatible with GraalVM Native Image out of the box. -
Here is how you can configure the SDK to work with GraalVM: +Enable automatic validation during startup: + +```java +MyAppFeatures.configure(ContextBuilder.builder() + .checkSwitchers(true) // Validate on initialization + // ... other config +); +``` + +### Native Image Support + +Switcher Client fully supports GraalVM Native Image compilation: ```java @ConfigurationProperties -public class MyNativeAppFeatureFlags extends SwitcherContextBase { - - public static final String MY_SWITCHER = "MY_SWITCHER"; +public class MyNativeAppFeatures extends SwitcherContextBase { + public static final String FEATURE_NEW_UI = "FEATURE_NEW_UI"; + public static final String FEATURE_PREMIUM = "FEATURE_PREMIUM"; + @Override @PostConstruct protected void configureClient() { - super.registerSwitcherKeys(MY_SWITCHER); + super.registerSwitcherKeys(FEATURE_NEW_UI, FEATURE_PREMIUM); super.configureClient(); } } ``` -Check out more code examples in the [Switcher Tutorials](https://github.com/switcherapi/switcherapi-tutorials) repository. + +--- + +#### Additional Resources + +- ๐Ÿ“š [Switcher Tutorials](https://github.com/switcherapi/switcherapi-tutorials) - Complete code examples and tutorials +- ๐ŸŒ [Switcher API Documentation](https://github.com/switcherapi/switcher-api) - Backend API documentation +- ๐Ÿ’ฌ [Join our Slack](https://switcher-hq.slack.com/) - Community support and discussions +- ๐Ÿ› [Report Issues](https://github.com/switcherapi/switcher-client-java/issues) - Bug reports and feature requests + +--- * * * *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/libjavascript.md b/src/assets/documentation/libjavascript.md index 6a08fcf..062e676 100644 --- a/src/assets/documentation/libjavascript.md +++ b/src/assets/documentation/libjavascript.md @@ -7,244 +7,388 @@ A JavaScript SDK for Switcher API *** -### Features -- Flexible and robust functions that will keep your code clean and maintainable. -- Able to work locally using a snapshot file downloaded from your remote Switcher-API Domain. -- Silent mode is a hybrid configuration that automatically enables a contingent sub-process in case of any connectivity issue. -- Built-in stub implementation for clear and easy implementation of automated testing. -- Easy to setup. Switcher Context is responsible to manage all the complexity between your application and API. +### Overview -* * * +**Switcher Client JS** is a feature-rich SDK for integrating [Switcher API](https://github.com/switcherapi/switcher-api) into your JS-based applications (Web, Node.js, Bun, Cloudflare Workers). It provides robust feature flag management with enterprise-grade capabilities. + +#### Key Features -### Usage +- ๐Ÿš€ **Zero Latency**: Local mode with snapshot files or in-memory for instant feature flag resolution +- ๐Ÿ”„ **Hybrid Configuration**: Silent mode with automatic fallback handling +- ๐Ÿงช **Testing Ready**: Built-in stub implementation for comprehensive testing +- โšก **Performance Optimized**: Throttling optimizes remote API calls to reduce bottlenecks in critical code paths +- ๐Ÿ› ๏ธ **Developer Tools**: Runtime snapshot updates without app restart and automatic sync with remote API -##### - Install -`npm install switcher-client` +--- -
+### Quick Start -##### - Module initialization -The context properties stores all information regarding connectivity. +Get up and running with Switcher Client in 3 simple steps: + +```bash +npm install switcher-client +``` ```js import { Client } from 'switcher-client'; -const apiKey = '[API_KEY]'; -const environment = 'default'; -const domain = 'My Domain'; -const component = 'MyApp'; -const url = 'https://api.switcherapi.com'; +// 1. Initialize the client +Client.buildContext({ + url: 'https://api.switcherapi.com', + apiKey: '[YOUR_API_KEY]', + domain: 'My Domain', + component: 'MyApp', + environment: 'default' +}); + +// 2. Get a switcher instance +const switcher = Client.getSwitcher(); + +// 3. Check if a feature is enabled +const isFeatureEnabled = await switcher.isItOn('FEATURE01'); +console.log('Feature enabled:', isFeatureEnabled); ``` -- **domain**: Domain name. -- **url**: (optional) Swither-API endpoint. -- **apiKey**: (optional) Switcher-API key generated to your component. -- **component**: (optional) Application name. -- **environment**: (optional) Environment name. Production environment is named as 'default'. +--- + +### Installation & Setup -##### - Options -You can also activate features such as local and silent mode: +#### Installation + +```bash +npm install switcher-client +``` + +#### Basic Configuration + +The context properties store all information regarding connectivity: ```js -const local = true; -const freeze = true; -const logger = true; -const snapshotLocation = './snapshot/'; -const snapshotAutoUpdateInterval = 3; -const snapshotWatcher = true; -const silentMode = '5m'; -const restrictRelay = true; -const certPath = './certs/ca.pem'; +import { Client } from 'switcher-client'; -Client.buildContext({ url, apiKey, domain, component, environment }, { - local, freeze, logger, snapshotLocation, snapshotAutoUpdateInterval, - snapshotWatcher, silentMode, restrictRelay, certPath -}); +// Required configuration +const config = { + apiKey: '[API_KEY]', // Switcher-API key for your component + environment: 'default', // Environment name ('default' for production) + domain: 'My Domain', // Your domain name + component: 'MyApp', // Your application name + url: 'https://api.switcherapi.com' // Switcher-API endpoint (optional) +}; +Client.buildContext(config); const switcher = Client.getSwitcher(); ``` -- **local**: If activated, the client will only fetch the configuration inside your snapshot file. The default value is 'false' -- **freeze**: If activated, prevents the execution of background cache update when using throttle. The default value is 'false' -- **logger**: If activated, it is possible to retrieve the last results from a given Switcher key using Client.getLogger('KEY') -- **snapshotLocation**: Location of snapshot files -- **snapshotAutoUpdateInterval**: Enable Snapshot Auto Update given an interval in seconds (default: 0 disabled) -- **snapshotWatcher**: Enable Snapshot Watcher to monitor changes in the snapshot file (default: false) -- **silentMode**: Enable contigency given the time for the client to retry - e.g. 5s (s: seconds - m: minutes - h: hours) -- **restrictRelay**: Enable Relay Restriction - Allow managing Relay restrictions when running in local mode (default: true) -- **regexSafe**: Enable REGEX Safe mode - Prevent agaist reDOS attack (default: true) -- **regexMaxBlackList**: Number of entries cached when REGEX Strategy fails to perform (reDOS safe) - default: 50 -- **regexMaxTimeLimit**: Time limit (ms) used by REGEX workers (reDOS safe) - default - 3000ms -- **certPath**: Path to the certificate file used to establish a secure connection with the API +##### Configuration Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `domain` | string | โœ… | Your Switcher domain name | +| `url` | string | | Switcher API endpoint | +| `apiKey` | string | | API key for your component | +| `component` | string | | Your application name | +| `environment` | string | | Environment name (default: 'default' for production) | + +#### Advanced Options + +Configure additional features for enhanced functionality: + +```ts +Client.buildContext({ + url, apiKey, domain, component, environment +}, { + local: true, // Enable local mode + freeze: false, // Prevent background updates + logger: true, // Enable request logging + snapshotLocation: './snapshot/', // Snapshot files directory + snapshotAutoUpdateInterval: 300, // Auto-update interval (seconds) + snapshotWatcher: true, // Monitor snapshot changes + silentMode: '5m', // Fallback timeout + restrictRelay: true, // Relay restrictions in local mode + regexSafe: true, // Prevent reDOS attacks + certPath: './certs/ca.pem' // SSL certificate path +}); +``` -(*) regexSafe is a feature that prevents your application from being exposed to a reDOS attack. It is recommended to keep this feature enabled.
+##### Options Reference -
+| Option | Type | Description | +|--------|------|-------------| +| `local` | boolean | Use only snapshot files/in-memory (no API calls) | +| `freeze` | boolean | Disable background cache updates with throttling | +| `logger` | boolean | Enable logging for debugging (`Client.getLogger('KEY')`) | +| `snapshotLocation` | string | Directory for snapshot files | +| `snapshotAutoUpdateInterval` | number | Auto-update interval in seconds (0 = disabled) | +| `snapshotWatcher` | boolean | Watch for snapshot file changes | +| `silentMode` | string | Fallback timeout (e.g., '5s', '2m', '1h') | +| `restrictRelay` | boolean | Enable relay restrictions in local mode | +| `regexSafe` | boolean | Protection against reDOS attacks | +| `regexMaxBlackList` | number | Max cached regex failures | +| `regexMaxTimeLimit` | number | Regex timeout in milliseconds | +| `certPath` | string | Path to SSL certificate file | -##### - Executing -There are a few different ways to call the API. -Here are some examples: +> **Security Note:** `regexSafe` prevents ReDoS attacks. Keep this enabled in production. -1. **Basic usage** -Some of the ways you can check if a feature is enabled or not. +--- + +### Usage Examples + +#### Basic Usage + +Multiple ways to check if a feature is enabled: ```js const switcher = Client.getSwitcher(); -// Local (synchronous) execution -const isOnBool = switcher.isItOn('FEATURE01'); // true or false -const isOnBool = switcher.isItOnBool('FEATURE01'); // true or false -const isOnDetail = switcher.detail().isItOn('FEATURE01'); // { result: true, reason: 'Success', metadata: {} } -const isOnDetail = switcher.isItOnDetail('FEATURE01'); // { result: true, reason: 'Success', metadata: {} } - -// Remote (asynchronous) execution or hybrid (local/remote) execution -const isOnBoolAsync = await switcher.isItOn('FEATURE01'); // Promise -const isOnBoolAsync = await switcher.isItOnBool('FEATURE01', true); // Promise -const isOnDetailAsync = await switcher.detail().isItOn('FEATURE01'); // Promise -const isOnDetailAsync = await switcher.isItOnDetail('FEATURE01', true); // Promise +// Synchronous (local mode only) +const isEnabled = switcher.isItOn('FEATURE01'); // Returns: boolean +const isEnabledBool = switcher.isItOnBool('FEATURE01'); // Returns: boolean +const detailResult = switcher.detail().isItOn('FEATURE01'); // Returns: { result, reason, metadata } +const detailDirect = switcher.isItOnDetail('FEATURE01'); // Returns: { result, reason, metadata } + +// Asynchronous (remote/hybrid mode) +const isEnabledAsync = await switcher.isItOn('FEATURE01'); // Returns: Promise +const isEnabledBoolAsync = await switcher.isItOnBool('FEATURE01', true); // Returns: Promise +const detailResultAsync = await switcher.detail().isItOn('FEATURE01'); // Returns: Promise +const detailDirectAsync = await switcher.isItOnDetail('FEATURE01', true); // Returns: Promise ``` -2. **Strategy validation - preparing input** -Loading information into the switcher can be made by using *prepare*, in case you want to include input from a different place of your code. Otherwise, it is also possible to include everything in the same call. +#### Strategy Validation + +##### Method 1: Prepare Input Separately + +Load information into the switcher using `prepare()` when input comes from different parts of your code: ```js +// Prepare the switcher with input data await switcher.checkValue('USER_1').prepare('FEATURE01'); -await switcher.isItOn(); + +// Execute the check +const result = await switcher.isItOn(); ``` -3. **Strategy validation - all-in-one execution** -All-in-one method is fast and include everything you need to execute a complex call to the API. +##### Method 2: All-in-One Execution + +Fast method that includes everything in a single call: ```js -await switcher - .defaultResult(true) // Default result to be returned in case of no API response - .throttle(1000) // Throttle the API call to improve performance - .checkValue('User 1') - .checkNetwork('192.168.0.1') - .isItOn('FEATURE01'); +const result = await switcher + .defaultResult(true) // Fallback result if API is unavailable + .throttle(1000) // Cache result for 1 second + .checkValue('User 1') // User-based strategy + .checkNetwork('192.168.0.1') // Network-based strategy + .isItOn('FEATURE01'); ``` -4. **Throttle** -Throttling is useful when placing Feature Flags at critical code blocks that require zero-latency. -API calls will be scheduled to be executed after the throttle time has passed. +#### Throttling + +Perfect for critical code blocks requiring zero-latency. API calls are scheduled after the throttle time: ```js const switcher = Client.getSwitcher(); -await switcher - .throttle(1000) - .isItOn('FEATURE01'); + +// Cache result for 1 second +const result = await switcher + .throttle(1000) + .isItOn('FEATURE01'); ``` -In order to capture issues that may occur during the process, it is possible to log the error by subscribing to the error events. +##### Error Handling for Throttled Calls + +Subscribe to error events to capture issues during throttled execution: ```js Client.subscribeNotifyError((error) => { - console.log(error); + console.error('Switcher error:', error); }); ``` -5. **Hybrid mode** -Forcing Switchers to resolve remotely can help you define exclusive features that cannot be resolved locally. -This feature is ideal if you want to run the SDK in local mode but still want to resolve a specific switcher remotely. +#### Hybrid Mode -A particular use case is when a swithcer has a Relay Strategy that requires a remote call to resolve the value. +Force specific switchers to resolve remotely while running in local mode. Ideal for features requiring remote validation (e.g., Relay Strategies): ```js const switcher = Client.getSwitcher(); -await switcher.remote().isItOn('FEATURE01'); + +// Force remote resolution for this specific call +const result = await switcher.remote().isItOn('FEATURE01'); ``` -
-## Built-in stub feature -You can also bypass your switcher configuration with 'Client.assume' API. This is perfect for your test code where you want to validate both scenarios when the switcher is true and false. +--- + +### Testing Features + +#### Built-in Stub Feature + +Bypass switcher configuration for testing scenarios. Perfect for validating both enabled and disabled states: + +##### Basic Stubbing ```js +// โœ… Force feature to be enabled Client.assume('FEATURE01').true(); -switcher.isItOn('FEATURE01'); // true +const result = switcher.isItOn('FEATURE01'); // Returns: true +// โŒ Force feature to be disabled +Client.assume('FEATURE01').false(); +const result = switcher.isItOn('FEATURE01'); // Returns: false + +// ๐Ÿ”„ Reset to normal behavior Client.forget('FEATURE01'); -switcher.isItOn('FEATURE01'); // Now, it's going to return the result retrieved from the API or the Snaopshot file +const result = switcher.isItOn('FEATURE01'); // Returns: actual API/snapshot result +``` + +##### Advanced Stubbing with Metadata + +```js +// Add custom metadata to simulate Relay responses +Client.assume('FEATURE01') + .false() + .withMetadata({ message: 'Feature is disabled' }); + +const response = await switcher.detail().isItOn('FEATURE01'); +console.log(response.result); // false +console.log(response.metadata.message); // "Feature is disabled" +``` + +##### Conditional Stubbing + +```js +import { StrategiesType } from 'switcher-client'; + +// โœ… True only for specific value +Client.assume('FEATURE01') + .true() + .when(StrategiesType.VALUE, 'USER_1'); -Client.assume('FEATURE01').false().withMetadata({ message: 'Feature is disabled' }); // Include metadata to emulate Relay response -const response = await switcher.detail().isItOn('FEATURE01'); // false -console.log(response.metadata.message); // Feature is disabled +const resultUser1 = switcher.checkValue('USER_1').isItOn('FEATURE01'); // true +const resultUser2 = switcher.checkValue('USER_2').isItOn('FEATURE01'); // false -Client.assume('FEATURE01').true().when(StrategiesType.VALUE, 'USER_1'); -switcher.checkValue('USER_1').isItOn('FEATURE01'); // true when the value is 'USER_1' +// โœ… True for multiple values +Client.assume('FEATURE01') + .true() + .when(StrategiesType.NETWORK, ['192.168.1.1', '192.168.1.2']); -Client.assume('FEATURE01').true().when(StrategiesType.NETWORK, ['USER_2', 'USER_3']); -switcher.checkValue('USER_1').isItOn('FEATURE01'); // false as the value is not in the list +const resultNetwork1 = switcher.checkNetwork('192.168.1.1').isItOn('FEATURE01'); // true +const resultNetwork2 = switcher.checkNetwork('192.168.1.3').isItOn('FEATURE01'); // false ``` -**Enabling Test Mode** -You may want to enable this feature while using Switcher Client with automated testing. -It prevents the Switcher Client from locking snapshot files even after the test execution. +#### Test Mode + +Enable test mode to prevent snapshot file locking during automated testing: -To enable this feature, it is recommended to place the following on your test setup files: ```js +// Add this to your test setup files Client.testMode(); ``` -**Smoke Test** -Validate Switcher Keys on your testing pipelines before deploying a change. -Switcher Keys may not be configured correctly and can cause your code to have undesired results. +> **๐Ÿ’ก Tip:** This prevents the Switcher Client from locking snapshot files even after test execution completes. -This feature will validate using the context provided to check if everything is up and running. -In case something is missing, this operation will throw an exception pointing out which Switcher Keys are not configured. -```js -Client.checkSwitchers(['FEATURE01', 'FEATURE02']) +#### Smoke Testing + +Validate feature flag during startup to catch configuration issues early: + +```ts +try { + await Client.checkSwitchers(['FEATURE01', 'FEATURE02', 'CRITICAL_FEATURE']); + console.log('โœ… All switchers configured correctly'); +} catch (error) { + console.error('โŒ Configuration issues found:', error.message); + process.exit(1); +} ``` -##### Loading Snapshot from the API -This step is optional if you want to load a copy of the configuration that can be used to eliminate latency when local mode is activated.
-Activate watchSnapshot optionally passing true in the arguments.
-Auto load Snapshot from API passing true as second argument. +This feature validates using the current context and throws an exception if any Switcher Keys are not properly configured. + +--- + +### Snapshot Management + +#### Loading Snapshots + +Load a local copy of your configuration to eliminate latency when local mode is activated: ```js -Client.loadSnapshot(); +// Basic snapshot loading +await Client.loadSnapshot(); + +// Load snapshot and enable auto-watching +await Client.loadSnapshot({ watchSnapshot: true }); + +// Fetch remote snapshot and enable auto-watching +await Client.loadSnapshot({ watchSnapshot: true, fetchRemote: true }); ``` -##### Watch for Snapshot file changes -Activate and monitor snapshot changes using this feature. Optionally, you can implement any action based on the callback response. +#### Watching for Changes + +##### Method 1: Programmatic Watching + +Monitor snapshot changes and implement custom actions: ```js Client.watchSnapshot({ - success: () => console.log('In-memory snapshot updated'), - reject: (err) => console.log(err) + success: () => console.log('โœ… In-memory snapshot updated'), + reject: (err) => console.error('โŒ Snapshot update failed:', err) }); ``` -Alternatively, you can also use the client context configuration to monitor changes in the snapshot file.
+##### Method 2: Configuration-based Watching + +Enable snapshot monitoring through client configuration: ```js -Client.buildContext({ domain, component, environment }, { +Client.buildContext( + { domain, component, environment }, + { local: true, snapshotLocation: './snapshot/', - snapshotWatcher: true -}); + snapshotWatcher: true // ๐Ÿ‘๏ธ Enable automatic monitoring + } +); ``` -##### - Snapshot version check -For convenience, an implementation of a domain version checker is available if you have external processes that manage snapshot files. +#### Version Checking + +Check if your snapshot is up to date with the remote domain: ```js -Client.checkSnapshot(); +try { + const versionInfo = await Client.checkSnapshot(); + console.log('Snapshot version info:', versionInfo); +} catch (error) { + console.error('Version check failed:', error); +} ``` -##### Snapshot Update Scheduler -You can also schedule a snapshot update using the method below.
-It allows you to run the Client SDK in local mode (zero latency) and still have the snapshot updated automatically. +> **Use Case:** Perfect for external processes that manage snapshot files independently. + +#### Auto-Update Scheduler + +Run the SDK in local mode (zero latency) while keeping snapshots automatically updated: ```js +// Update every 3 seconds (3000 milliseconds) Client.scheduleSnapshotAutoUpdate(3000, { success: (updated) => console.log('Snapshot updated', updated), - reject: (err) => console.log(err) + reject: (err: Error) => console.log(err) }); ``` +##### Alternative: Configuration-based Auto-Update + +```js +Client.buildContext( + { domain, component, environment }, + { + local: true, + snapshotAutoUpdateInterval: 60 // ๐Ÿ• Update every 60 seconds + } +); +``` + * * * *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/metrics.md b/src/assets/documentation/metrics.md index 977e010..7c9b490 100644 --- a/src/assets/documentation/metrics.md +++ b/src/assets/documentation/metrics.md @@ -5,7 +5,7 @@ Monitoring is a great feature for you to keep track of every call from your regi When your application/component calls the API, it will store the result of this execution afterwards. It means that if the switcher is turned off, the reason that will be recorded is 'Config disabled'. In case you have configured a specific strategy that allows the use of that feature for a specific range of IPs, for instance, it will record whether input matched or not. - +

@@ -17,12 +17,12 @@ When visualizing Overall Statistics, it is possible to find insights into the fo Quantifies how many negative and positive calls were made by switchers. -

+

Get a more detailed view by clicking on one specific switcher bar.
It will automatically open the filter mode to select the time frame, group by option and environment. - +

@@ -31,7 +31,7 @@ When visualizing Overall Statistics, it is possible to find insights into the fo Navigate through the time frame to visualize the execution history. By clicking on one of the nodes you can also open the detailed Data View for that specific date-time. - +

@@ -40,7 +40,7 @@ When visualizing Overall Statistics, it is possible to find insights into the fo Quantified per component. Here you can track how your components are behaving. - +

@@ -48,7 +48,7 @@ When visualizing Overall Statistics, it is possible to find insights into the fo * * * Quantitative overall view of all executed results for a specific switcher. - +

@@ -57,12 +57,12 @@ When visualizing Overall Statistics, it is possible to find insights into the fo The Data View displays more detailed information about the API criteria execution.
This view is perfect for troubleshooting and double-check what information is being used to call the API. - + * * * *Did you find an error? Please, open an issue* - + diff --git a/src/assets/documentation/overview.md b/src/assets/documentation/overview.md index bd3c225..66b872f 100644 --- a/src/assets/documentation/overview.md +++ b/src/assets/documentation/overview.md @@ -70,5 +70,5 @@ Sometimes covering 100% of tests does not guarantee a successful deployment, nor *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/relay.md b/src/assets/documentation/relay.md index a732ab9..0262992 100644 --- a/src/assets/documentation/relay.md +++ b/src/assets/documentation/relay.md @@ -6,7 +6,7 @@ Not only can applications make use of all the built-in strategy validations, but This document will show you the different ways you can configure it and how to implement the REST API receptor, which will be responsible for receiving and processing the information provided by the Switcher Relay. - +

@@ -19,7 +19,7 @@ Below you will learn how to configure a Switcher Relay and its results after it After creating a Switcher, you can add one Relay per-environment by clicking on *Add Relay*. A new view will be showed right below your switcher main view. This panel must contains all the related information regarding how to access your REST receptor. -

+

This view is composed by the settings: @@ -55,7 +55,7 @@ This option will make sure that any Relay endpoint in use belongs to the organiz When this option is enabled on Switcher API using [`RELAY_BYPASS_VERIFICATION = false`], Switcher Management will display the Verify button at the bottom of the Relay details view.
A generated code will be provided and further details will be shown on how to implement your Relay verification in simple 3 steps. -

+



@@ -135,7 +135,7 @@ router.post('/validate', (req, res) => { Defining strategies before Relays are the best way to keep the consistency of all external validation. It creates a smart gateway between your application and the external API used to validate or being notified. - +

@@ -144,7 +144,7 @@ router.post('/validate', (req, res) => { Below you can access the result given a possible execution of this validation made via Switcher Relay where the user on the context is not allowed to access the feature. - +

@@ -152,5 +152,5 @@ router.post('/validate', (req, res) => { *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/setup.md b/src/assets/documentation/setup.md index 34daa79..fd909d8 100644 --- a/src/assets/documentation/setup.md +++ b/src/assets/documentation/setup.md @@ -4,7 +4,7 @@ The 5 steps show the minimum configuration necessary to start using Switcher API on your projects.
As you complete all these steps, take a look at the Java or JavaScript modules to start setting up your application to communicate with the Switcher API. - +

@@ -23,7 +23,7 @@ This is going to be your workspace which will contain: - Environment and application settings - Team management - +

@@ -32,12 +32,12 @@ This is going to be your workspace which will contain: Configure here all your projects/applications that will make use of this API. This step is required for you to keep track of everything and access Switcher API.

After registering your component, it will be available for you to link with any switcher. -


+


At the end of this step, you'll be given an API Key that will be used exclusively for the registered component. For security purposes, this key cannot be reclaimed, so keep it in a safe place. -

+



@@ -62,11 +62,11 @@ Finally, define your switcher by giving a key value and a brief description. - Alphanumeric - No spaces - + * * * *Did you find an error? Please, open an issue* - + diff --git a/src/assets/documentation/shortcuts.md b/src/assets/documentation/shortcuts.md index a119a2b..89e09a6 100644 --- a/src/assets/documentation/shortcuts.md +++ b/src/assets/documentation/shortcuts.md @@ -9,17 +9,17 @@ We tried to keep things as simple as possible, reducing the complexity behind th This simple input text found on the right side of your Domain's menu can easily assist you to find the configuration element you are looking for. -


+


Here are some examples of how it works. 1. If you are looking for Switchers used by a specific component, you can just type the component's name that it will bring all switchers linked to it. -


+


2. You can also filter elements by its prefix: 's:' for Switcher, 'g:' for Groups and 'c:' for Components -


+


3. The search algorithm is also prepared for looking at elements description, thus it can be a good practice to add tags or keywords on all groups and switchers that you create. @@ -33,7 +33,7 @@ We tried to keep things as simple as possible, reducing the complexity behind th That means that the same input can find the information you are looking at in all columns of both Data Metrics and Change Log tables. -


+




@@ -42,7 +42,7 @@ We tried to keep things as simple as possible, reducing the complexity behind th Switcher Management is fully responsive to work with different viewports. Considering these variations, we built the best and simple layout for all scenarios. -

+



@@ -50,5 +50,5 @@ We tried to keep things as simple as possible, reducing the complexity behind th *Did you find an error? Please, open an issue* - + diff --git a/src/assets/documentation/slack_features.md b/src/assets/documentation/slack_features.md index 93fdd25..041afac 100644 --- a/src/assets/documentation/slack_features.md +++ b/src/assets/documentation/slack_features.md @@ -1,4 +1,4 @@ -

+

#### Change Request * * * @@ -10,7 +10,7 @@ The Change Request feature is a powerful tool that allows you to request changes To create a ticket you just need to provide the necessary details such as Domain, Environment, Feature Group, Switcher name and status.
It is also possible to select Group Features to toggle multiple features at once. -

+



@@ -19,7 +19,7 @@ It is also possible to select Group Features to toggle multiple features at once After defining the attributes of your ticket, you can review and add an observation that is useful to give more details about the change request.
The app can also identify if a ticket already exists and republish it to the approval channel. Validations are also executed to make sure that the change request is valid. -

+



@@ -28,7 +28,7 @@ The app can also identify if a ticket already exists and republish it to the app When submitted, the ticket is automatically published to the approval channel, where the approvers can review and approve or deny the change request.
Once the ticket is approved or denied, the message is updated with the action taken, and the event is sent to Switcher API. -

+



@@ -36,5 +36,5 @@ Once the ticket is approved or denied, the message is updated with the action ta *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/slack_installation.md b/src/assets/documentation/slack_installation.md index 96c2706..36a22a9 100644 --- a/src/assets/documentation/slack_installation.md +++ b/src/assets/documentation/slack_installation.md @@ -5,7 +5,7 @@ Find here all the necessary steps to install **Switcher Slack App** to your Slac The app was developed primarily to improve visibility and actions taken by your team, hence, in order to provide a secured environment, the app installation requires that the Domain owner acknowledge the necessary permissions to both Slack and Switcher API to work properly. -

+

#### Requirements * * * @@ -20,7 +20,7 @@ Make sure you have a channel created beforehand which will be used to publish ch To start the installation process, you can find the installation button on any Domain Details view under the toolbar menu.
Clicking on the Install Slack App will not yet link the installation with the current Domain, but it will start the installation process. -

+



@@ -29,7 +29,7 @@ Clicking on the Install Slack App will not yet link the installation with the cu A new page will be shown to confirm that you want to add the Slack App to your workspace.
When this page is loaded, a temporary token access is generated to link the installation with the a Doamin to be selected in the step 4. -

+



@@ -40,7 +40,7 @@ Switcher Slack App works with minimum access to Workspaces. Current scopes are ` After verifying all accesses and permissions, select the channel in which the app will use to publish change requests.
-

+



@@ -50,7 +50,7 @@ Switcher API also requires you to authorize that Slack App can perform requests The two-hands shaking guarantee that both platforms can interact with each other.
As mentioned before in the step 2, you will be prompted to select a Domain to link the Slack App installation (only available Domains will be shown). -

+



@@ -58,18 +58,18 @@ As mentioned before in the step 2, you will be prompted to select a Domain to li Now that both platforms are linked, you can see that a new integration was added to the channel. -

+

Click on the **Switch API** link to first add the app to the channel so messages can be published from the Switcher Slack App.
Once that is completed, you can click on the **Go to App** to open the Switcher Slack App and start using it. -

+

* * * *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/assets/documentation/strategies.md b/src/assets/documentation/strategies.md index d9b7f56..b6dc5ce 100644 --- a/src/assets/documentation/strategies.md +++ b/src/assets/documentation/strategies.md @@ -53,7 +53,7 @@ For each switcher it is possible to add at least 4 different types of strategy. Payload validations are useful to validate keys from a JSON input request.
The available operations for this validator are: Has one, Has all. - +

@@ -63,7 +63,7 @@ For each switcher it is possible to add at least 4 different types of strategy. Strategies are defined per environment, adding more flexibility for you to specify conditions that are not shared or common among environments.
To simplify, it is possible to clone a created strategy instead of starting from scratch. -

+



@@ -72,7 +72,7 @@ Strategies are defined per environment, adding more flexibility for you to speci Keep control of what is being sent as input to strategies. Under Metrics - Data tab view, besides the result of each call executed you can see what it was sent for each strategy. -

+



@@ -80,5 +80,5 @@ Keep control of what is being sent as input to strategies. Under Metrics - Data *Did you find an error? Please, open an issue* - + diff --git a/src/assets/documentation/team.md b/src/assets/documentation/team.md index e788a2f..427cad6 100644 --- a/src/assets/documentation/team.md +++ b/src/assets/documentation/team.md @@ -7,7 +7,7 @@ Teams are composed of Members and Permissions. In addition to permissions that w Get extra help while keeping control of who can create, read, update, or delete inside your domain. - +

@@ -19,16 +19,16 @@ After creating a team, you can start setting up the new team either way, invitin Using the field as shown below to filter or inviting members. -

+

After clicking on the invite button, a link will be provided to share with the invited member. -

+

A generated link contains a request ID that link the contact member with the Domain/Team. For the invited member to proceed, it is required to be logged in in order to complete the request action. -

+

Shared Domains are displayed on the Dashboard as Collaboration. @@ -39,7 +39,7 @@ After creating a team, you can start setting up the new team either way, invitin Visualize all ongoing invitations by accessing the **Pending** tab as well as recovery the invitation link. - +

@@ -49,7 +49,7 @@ After creating a team, you can start setting up the new team either way, invitin By clicking on the Create button, you can select one of the elements available in addition to the permission that will be granted to.
The new permission is a combination of rules that can also be configured by specifing one or more component names. -

+

The available components are: - **All**: The permission will applies to all components. @@ -68,5 +68,5 @@ The available actions are: *Did you find an error? Please, open an issue* - + \ No newline at end of file diff --git a/src/libs/ng-recaptcha-module/lib/load-script.ts b/src/libs/ng-recaptcha-module/lib/load-script.ts index f6a6b06..e349eee 100644 --- a/src/libs/ng-recaptcha-module/lib/load-script.ts +++ b/src/libs/ng-recaptcha-module/lib/load-script.ts @@ -17,8 +17,8 @@ function loadScript( onLoaded: (grecaptcha: ReCaptchaV2.ReCaptcha) => void, { url, lang, nonce }: { url?: string; lang?: string; nonce?: string } = {}, ): void { - window.ng2recaptchaloaded = () => { - onLoaded(window.grecaptcha); + globalThis.ng2recaptchaloaded = () => { + onLoaded(globalThis.grecaptcha); }; const script = document.createElement("script"); script.innerHTML = ""; diff --git a/src/libs/ng-recaptcha-module/lib/tokens.ts b/src/libs/ng-recaptcha-module/lib/tokens.ts index 70e863d..7c4689d 100644 --- a/src/libs/ng-recaptcha-module/lib/tokens.ts +++ b/src/libs/ng-recaptcha-module/lib/tokens.ts @@ -75,7 +75,7 @@ export interface RecaptchaLoaderOptions { * Allows you to change the `grecaptcha` that the `ng-recaptcha` will be relying on. * This method is useful when you need to use `grecaptcha.enterprise` instead of the base `grecaptcha` * - * @param recaptcha the value of `window.grecaptcha` upon script load. + * @param recaptcha the value of `globalThis.grecaptcha` upon script load. * @returns the {ReCaptchaV2.ReCaptcha} instance that the `ng-recaptcha` lib will use. * * @example