diff --git a/.devcontainer/create_cert b/.devcontainer/create_cert index 8ee607f..dba0dac 100755 --- a/.devcontainer/create_cert +++ b/.devcontainer/create_cert @@ -2,7 +2,8 @@ # create self-signed certificate for testing with the browser -HOST=data.staging.ghga.dev +BASE_URL=${BASE_URL:-https://data.staging.ghga.dev} +HOST=${BASE_URL#https://} CERTFILE=cert.pem KEYFILE=key.pem diff --git a/.devcontainer/data-portal.yaml b/.devcontainer/data-portal.yaml index 5139827..4fe7b96 100644 --- a/.devcontainer/data-portal.yaml +++ b/.devcontainer/data-portal.yaml @@ -11,5 +11,9 @@ log_level: DEBUG base_url: http://127.0.0.1:8080 +auth_url: /api/auth mass_url: /api/mass metldata_url: /api/metldata + +oidc_client_id: ghga-dev-client +oidc_authority_url: https://login.aai.lifescience-ri.eu/oidc/ diff --git a/.devcontainer/dev_install b/.devcontainer/dev_install index 6f716f5..130549e 100755 --- a/.devcontainer/dev_install +++ b/.devcontainer/dev_install @@ -3,7 +3,7 @@ cd /workspace -corepack prepare pnpm@9.13.2 --activate +corepack prepare pnpm@9.14.2 --activate echo "Updating pnpm..." npm install -g pnpm diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f1e146b..42972fc 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,10 @@ // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [8080, 443], + "forwardPorts": [ + 8080, + 443 + ], // Create an empty local env file if it does not yet exist "initializeCommand": "touch .devcontainer/local.env", // Use 'postCreateCommand' to run commands after the container is created. @@ -29,7 +32,10 @@ }, "editor.formatOnSave": true, "editor.renderWhitespace": "all", - "editor.rulers": [88, 120], + "editor.rulers": [ + 88, + 120 + ], "editor.defaultFormatter": "vscode.typescript-language-features" }, // Add the IDs of extensions you want installed when the container is created. diff --git a/.prettierrc b/.prettierrc index df55829..917cd5d 100644 --- a/.prettierrc +++ b/.prettierrc @@ -4,5 +4,6 @@ "useTabs": false, "singleQuote": true, "printWidth": 88, - "proseWrap": "preserve" + "proseWrap": "preserve", + "endOfLine": "lf" } diff --git a/.vscode/settings.json b/.vscode/settings.json index 9ace906..4f1750a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,11 @@ { "window.title": "Data Portal - ${dirty}${activeEditorShort}${separator}${rootName}${separator}${profileName}${separator}${appName}", - "cSpell.words": ["devcontainer", "devkit", "metldata"], + "cSpell.words": ["devcontainer", "devkit", "metldata", "TOTP"], "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, + "[html]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, @@ -18,5 +21,7 @@ "eslint.options": { "overrideConfigFile": "/workspace/eslint.config.js" }, - "remote.localPortHost": "allInterfaces" + "remote.localPortHost": "allInterfaces", + "files.insertFinalNewline": true, + "files.trimFinalNewlines": true } diff --git a/Dockerfile b/Dockerfile index 3bb2f04..6831af0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,7 +5,7 @@ RUN apk upgrade --no-cache --available # BUILDER: a container to build the service dist directory FROM base AS builder # install pnpm -RUN corepack prepare pnpm@9.13.2 --activate +RUN corepack prepare pnpm@9.14.2 --activate RUN npm install -g pnpm # install static web server RUN apk add curl sudo which @@ -28,8 +28,7 @@ COPY --from=builder /service/dist/data-portal/browser ./dist COPY --from=builder /usr/local/bin/static-web-server /usr/local/bin # make the config file writeable to the appuser USER root -RUN touch ./dist/config.js -RUN chown appuser ./dist/config.js +RUN touch ./dist/config.js && chown appuser ./dist/config.js USER appuser # install run script COPY ./run.js ./run.mjs diff --git a/README.md b/README.md index 3ccf9d7..7bc016c 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,14 @@ If the staging backend requires an additional Basic authentication, you can set dev_launcher staging username:password ``` +In order to make the OIDC and basic authentication work, you also need to add a `.devcontainer/local.env` file like this, with the proper credentials: + +```env +data_portal_base_url=https://data.staging.ghga.dev +data_portal_basic_auth=USERNAME:PASSWORD +data_portal_oidc_client_id=OIDC_DEV_CLIENT_ID +``` + ## Code scaffolding Angular CLI includes powerful code scaffolding tools. To generate a new component, run: diff --git a/data-portal.default.yaml b/data-portal.default.yaml index d9a7ed0..19ec6fe 100644 --- a/data-portal.default.yaml +++ b/data-portal.default.yaml @@ -13,5 +13,15 @@ log_level: INFO base_url: http://127.0.0.1:8080 +auth_url: /api/auth mass_url: /api/mass metldata_url: /api/metldata + +oidc_client_id: ghga-client +oidc_redirect_url: oauth/callback +oidc_scope: 'openid profile email' +oidc_authority_url: https://login.aai.lifescience-ri.eu/oidc/ +oidc_authorization_url: authorize +oidc_token_url: token +oidc_userinfo_url: userinfo +oidc_use_discovery: true diff --git a/eslint.config.js b/eslint.config.js index 1dc2511..cd2bef4 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -131,21 +131,17 @@ export default [ { from: ['config'], disallow: ['*'], - message: 'Config modules can only import modules with routes', + message: 'Config modules can only import routes and auth services', }, { from: ['config'], - allow: ['routes'], + allow: ['routes', ['service', { context: 'auth' }]], }, - // modules for routes may only import feature components + // modules for routes may not import ui components { from: ['routes'], - disallow: ['*'], - message: 'Modules for routes can only import feature components', - }, - { - from: ['routes'], - allow: ['features'], + disallow: ['ui'], + message: 'Modules for routes cannot import ui components', }, // unit tests are currently exempt from all rules { @@ -180,7 +176,7 @@ export default [ }, // Auth service may be imported in other contexts { - from: ['features', 'service'], + from: ['features', 'service', 'routes'], allow: [['service', { context: 'auth' }]], }, // Auth models may be imported in other contexts diff --git a/package.json b/package.json index e8f1829..9c9e2ad 100644 --- a/package.json +++ b/package.json @@ -20,38 +20,40 @@ "type": "module", "private": true, "dependencies": { - "@angular-eslint/eslint-plugin": "^18.4.1", - "@angular-eslint/template-parser": "^18.4.1", - "@angular/animations": "19.0.0-rc.3", - "@angular/cdk": "19.0.0-rc.3", - "@angular/common": "19.0.0-rc.3", - "@angular/compiler": "19.0.0-rc.3", - "@angular/core": "19.0.0-rc.3", - "@angular/forms": "19.0.0-rc.3", - "@angular/material": "19.0.0-rc.3", - "@angular/platform-browser": "19.0.0-rc.3", - "@angular/platform-browser-dynamic": "19.0.0-rc.3", - "@angular/router": "19.0.0-rc.3", + "@angular-eslint/eslint-plugin": "^18.4.2", + "@angular-eslint/template-parser": "^18.4.2", + "@angular/animations": "^19.0.1", + "@angular/cdk": "^19.0.1", + "@angular/common": "^19.0.1", + "@angular/compiler": "^19.0.1", + "@angular/core": "^19.0.1", + "@angular/forms": "^19.0.1", + "@angular/material": "^19.0.1", + "@angular/platform-browser": "^19.0.1", + "@angular/platform-browser-dynamic": "^19.0.1", + "@angular/router": "^19.0.1", + "angularx-qrcode": "^19.0.0", "js-yaml": "^4.1.0", + "oidc-client-ts": "^3.1.0", "rxjs": "~7.8.1", "tslib": "^2.8.1" }, "devDependencies": { - "@angular-devkit/build-angular": "19.0.0-rc.3", - "@angular/cli": "19.0.0-rc.3", - "@angular/compiler-cli": "19.0.0-rc.3", + "@angular-devkit/build-angular": "^19.0.2", + "@angular/cli": "^19.0.2", + "@angular/compiler-cli": "^19.0.1", "@compodoc/compodoc": "^1.1.26", "@eslint/markdown": "^6.2.1", "@types/jasmine": "~5.1.4", - "@typescript-eslint/eslint-plugin": "^8.15.0", - "@typescript-eslint/parser": "^8.15.0", - "angular-eslint": "^18.4.1", + "@typescript-eslint/eslint-plugin": "^8.16.0", + "@typescript-eslint/parser": "^8.16.0", + "angular-eslint": "19.0.0-alpha.4", "autoprefixer": "^10.4.20", "eslint": "^9.15.0", "eslint-config-prettier": "^9.1.0", "eslint-import-resolver-typescript": "^3.6.3", "eslint-plugin-boundaries": "^5.0.1", - "eslint-plugin-jsdoc": "^50.5.0", + "eslint-plugin-jsdoc": "^50.6.0", "eslint-plugin-prettier": "^5.2.1", "husky": "^9.1.7", "jasmine-core": "~5.4.0", @@ -62,9 +64,9 @@ "karma-jasmine-html-reporter": "~2.1.0", "postcss": "^8.4.49", "prettier": "3.3.3", - "prettier-plugin-tailwindcss": "^0.6.8", + "prettier-plugin-tailwindcss": "^0.6.9", "tailwindcss": "^3.4.15", "typescript": "~5.6.3", - "typescript-eslint": "^8.15.0" + "typescript-eslint": "^8.16.0" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1c72984..b202213 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,44 +9,50 @@ importers: .: dependencies: '@angular-eslint/eslint-plugin': - specifier: ^18.4.1 - version: 18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + specifier: ^18.4.2 + version: 18.4.2(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) '@angular-eslint/template-parser': - specifier: ^18.4.1 - version: 18.4.1(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + specifier: ^18.4.2 + version: 18.4.2(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) '@angular/animations': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + specifier: ^19.0.1 + version: 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) '@angular/cdk': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + specifier: ^19.0.1 + version: 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) '@angular/common': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + specifier: ^19.0.1 + version: 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) '@angular/compiler': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + specifier: ^19.0.1 + version: 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) '@angular/core': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + specifier: ^19.0.1 + version: 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) '@angular/forms': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) + specifier: ^19.0.1 + version: 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) '@angular/material': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(2ggkf5bmbgan54h663qv7i4v7a) + specifier: ^19.0.1 + version: 19.0.1(caeb6eiffeog75fr4ymtnaa34q) '@angular/platform-browser': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + specifier: ^19.0.1 + version: 19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) '@angular/platform-browser-dynamic': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))) + specifier: ^19.0.1 + version: 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))) '@angular/router': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) + specifier: ^19.0.1 + version: 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) + angularx-qrcode: + specifier: ^19.0.0 + version: 19.0.0(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) js-yaml: specifier: ^4.1.0 version: 4.1.0 + oidc-client-ts: + specifier: ^3.1.0 + version: 3.1.0 rxjs: specifier: ~7.8.1 version: 7.8.1 @@ -55,14 +61,14 @@ importers: version: 2.8.1 devDependencies: '@angular-devkit/build-angular': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.9.0)(chokidar@4.0.1)(karma@6.4.4)(tailwindcss@3.4.15)(typescript@5.6.3)(vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + specifier: ^19.0.2 + version: 19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.0)(chokidar@4.0.1)(karma@6.4.4)(tailwindcss@3.4.15)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) '@angular/cli': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@types/node@22.9.0)(chokidar@4.0.1) + specifier: ^19.0.2 + version: 19.0.2(@types/node@22.10.0)(chokidar@4.0.1) '@angular/compiler-cli': - specifier: 19.0.0-rc.3 - version: 19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) + specifier: ^19.0.1 + version: 19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) '@compodoc/compodoc': specifier: ^1.1.26 version: 1.1.26(typescript@5.6.3) @@ -73,14 +79,14 @@ importers: specifier: ~5.1.4 version: 5.1.4 '@typescript-eslint/eslint-plugin': - specifier: ^8.15.0 - version: 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + specifier: ^8.16.0 + version: 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) '@typescript-eslint/parser': - specifier: ^8.15.0 - version: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + specifier: ^8.16.0 + version: 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) angular-eslint: - specifier: ^18.4.1 - version: 18.4.1(@angular-devkit/core@19.0.0-rc.3(chokidar@4.0.1))(@angular-devkit/schematics@19.0.0-rc.3(chokidar@4.0.1))(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript-eslint@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(typescript@5.6.3) + specifier: 19.0.0-alpha.4 + version: 19.0.0-alpha.4(@angular-devkit/core@19.0.2(chokidar@4.0.1))(@angular-devkit/schematics@19.0.2(chokidar@4.0.1))(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript-eslint@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(typescript@5.6.3) autoprefixer: specifier: ^10.4.20 version: 10.4.20(postcss@8.4.49) @@ -92,13 +98,13 @@ importers: version: 9.1.0(eslint@9.15.0(jiti@1.21.6)) eslint-import-resolver-typescript: specifier: ^3.6.3 - version: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)) + version: 3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)) eslint-plugin-boundaries: specifier: ^5.0.1 - version: 5.0.1(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) + version: 5.0.1(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) eslint-plugin-jsdoc: - specifier: ^50.5.0 - version: 50.5.0(eslint@9.15.0(jiti@1.21.6)) + specifier: ^50.6.0 + version: 50.6.0(eslint@9.15.0(jiti@1.21.6)) eslint-plugin-prettier: specifier: ^5.2.1 version: 5.2.1(@types/eslint@9.6.1)(eslint-config-prettier@9.1.0(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6))(prettier@3.3.3) @@ -130,8 +136,8 @@ importers: specifier: 3.3.3 version: 3.3.3 prettier-plugin-tailwindcss: - specifier: ^0.6.8 - version: 0.6.8(prettier@3.3.3) + specifier: ^0.6.9 + version: 0.6.9(prettier@3.3.3) tailwindcss: specifier: ^3.4.15 version: 3.4.15 @@ -139,8 +145,8 @@ importers: specifier: ~5.6.3 version: 5.6.3 typescript-eslint: - specifier: ^8.15.0 - version: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + specifier: ^8.16.0 + version: 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) packages: @@ -155,25 +161,25 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-devkit/architect@0.1900.0-rc.3': - resolution: {integrity: sha512-unCfZQ6akI8CnMN2WJgPkdXSm1z+K1iJlbzbIaoidVeuh4Yt5RiJqvarm/1rfdBhBMSvheIvycK2zPqSWBk3cA==} + '@angular-devkit/architect@0.1900.2': + resolution: {integrity: sha512-rGUgOgN/jb3Pyx3E1JsUbwQQZp4C0M/t0lwyWIFjUpndl27aBDjO2y5hzeG0B1+FgOuSNg8BPOYaEIO5vSCspw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/build-angular@19.0.0-rc.3': - resolution: {integrity: sha512-gh1OYsoChngmhBCeACRqyQw62KOvzrI68coXH80tVvUDy6v/0de87CFlbndIKt3voZyavzTi+d230QB7dl3pdw==} + '@angular-devkit/build-angular@19.0.2': + resolution: {integrity: sha512-F7wwo0fVshrlnTyBuqP6abt95soOsO+H/dYLn0JVud+SXhbSXpKDxZovlIBUKh1kj0BXny7erTYHmPWVtZpfsg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler-cli': ^19.0.0-next.0 - '@angular/localize': ^19.0.0-next.0 - '@angular/platform-server': ^19.0.0-next.0 - '@angular/service-worker': ^19.0.0-next.0 - '@angular/ssr': ^19.0.0-rc.3 + '@angular/compiler-cli': ^19.0.0 + '@angular/localize': ^19.0.0 + '@angular/platform-server': ^19.0.0 + '@angular/service-worker': ^19.0.0 + '@angular/ssr': ^19.0.2 '@web/test-runner': ^0.19.0 browser-sync: ^3.0.2 jest: ^29.5.0 jest-environment-jsdom: ^29.5.0 karma: ^6.3.0 - ng-packagr: ^19.0.0-next.0 + ng-packagr: ^19.0.0 protractor: ^7.0.0 tailwindcss: ^2.0.0 || ^3.0.0 typescript: '>=5.5 <5.7' @@ -203,8 +209,8 @@ packages: tailwindcss: optional: true - '@angular-devkit/build-webpack@0.1900.0-rc.3': - resolution: {integrity: sha512-g9OqbmmE6nsdpfZBCGAu0u4kuIm/lpOEiKOwQKYt4gk5gCKexPAgIbnKyc04oE+ZtagHdwZflElej0qR544SMg==} + '@angular-devkit/build-webpack@0.1900.2': + resolution: {integrity: sha512-4JHkY6908YsIWh9FM/6ihsVZyWAM4/C91D8S4v/aZhVLt37HwTAxbecPbYNbexgDca81LI5TAqR8cwb0syIkWA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 @@ -219,8 +225,8 @@ packages: chokidar: optional: true - '@angular-devkit/core@19.0.0-rc.3': - resolution: {integrity: sha512-SNtTFkGHgvOLU71cJvr1kvX02ZeLF5N8jr3oQX6ToX7wQwWorjMF7YZ0StNK2+ADYKv1OypteLfjf+K3VcUYDA==} + '@angular-devkit/core@19.0.2': + resolution: {integrity: sha512-p5pTx9rAtJUfoa7BP6R5U7dGFWHrrgpYpVyF3jwqYIu0h1C0rJIyY8q/HlkvzFxgfWag1qRf15oANq3G9fqdwg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^4.0.0 @@ -232,69 +238,92 @@ packages: resolution: {integrity: sha512-i/h2Oji5FhJMC7wDSnIl5XUe/qym+C1ZwScaATJwDyRLCUIynZkj5rLgdG/uK6l+H0PgvxigkF+akWpokkwW6w==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/schematics@19.0.0-rc.3': - resolution: {integrity: sha512-g377hBz2c/jb2xw0wD77sN6IrdTKTBhfuanT6xT27llLf0e4r7gh7DMKteUF+sigYsR6jMUCwl/+Dne6LVF3hA==} + '@angular-devkit/schematics@19.0.2': + resolution: {integrity: sha512-bwq8ReC92gGFTd2BeNBWCnOqIKu2YKNvwMVc7dl+D154WO2gzCaK2J5nL97qm5EjoUoXgvFRs84ysSAnLFzBxQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-eslint/builder@18.4.1': - resolution: {integrity: sha512-Ofkwd9Rg52K+AgvnV1RXYXVBGJvl5jD7+4dqwoprqXG7YKNTdHy5vqNZ5XDSMb26qjoZF7JC+IKruKFaON/ZaA==} + '@angular-eslint/builder@19.0.0-alpha.4': + resolution: {integrity: sha512-iSDl0Hs2fkJJH0aR/RQ80nmickY7o1xv+mucSw/Gy4YwFDJFU0FiV++1OxhjturuEXt3k+TJ115xe4DJa86BMw==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular-eslint/bundled-angular-compiler@18.4.1': - resolution: {integrity: sha512-gCQC0mgBO1bwHDXL9CUgHW+Rf1XGZCLAopoXnggwxGkBCx+oww507t+jrSOxdh+4OTKU4ZfmbtWd7Y8AeXns8w==} + '@angular-eslint/bundled-angular-compiler@18.4.2': + resolution: {integrity: sha512-K7pqmZI3Dl75zlLexyaM7bw4xdgk/3bhP1B6uqDKML9+vIIvccCR2bGvqFurqeFbJlMykzb3H4jytT+HpqV4tg==} - '@angular-eslint/eslint-plugin-template@18.4.1': - resolution: {integrity: sha512-sofnKpi6wOZ6avVfYYqB7sCgGgWF2HgCZfW+IAp1MtVD2FBa1zTSbbfIZ1I8Akpd22UXa4LKJd0TLwm5XHHkiQ==} + '@angular-eslint/bundled-angular-compiler@19.0.0-alpha.4': + resolution: {integrity: sha512-SS2FHqRaGslJzI+cTBNDC7xg/Zx5c0iIXZnpwGa8VjJ/8L82+PlRS+d9CTBhb8tMsR06ifUTK9ym2JQ3VmE2Cg==} + + '@angular-eslint/eslint-plugin-template@19.0.0-alpha.4': + resolution: {integrity: sha512-YiFB+tyTZ/mj/w/5DLJHl9J1ABGaHNhGdXJzagI0ufqyrePR0wTYMIyJpIGWOMHr9E5nYPsVuHn+d08dG1R0aQ==} peerDependencies: '@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 typescript: '*' - '@angular-eslint/eslint-plugin@18.4.1': - resolution: {integrity: sha512-FoHwj+AFo8ONKb8wEK5qpo6uefuyklZlDqErJxeC3fpNIJzDe8PWBcJsuZt7Wwm/HeggWgt0Au6h+3IEa0V3BQ==} + '@angular-eslint/eslint-plugin@18.4.2': + resolution: {integrity: sha512-Oem4W2P54cPADN9rJenLj90rqDPUQWx5kZiz84FCnsSn5DBdsI5LGQoogNT9y3Jx/9VL/VGIMMA5B6qG+0hVlg==} peerDependencies: '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular-eslint/schematics@18.4.1': - resolution: {integrity: sha512-1+gGodwh+UevtEx9mzZbzP1uY/9NAGEbsn8jisG1TEPDby2wKScQj6U6JwGxoW/Dd/4SIeSdilruZPALkqha7g==} + '@angular-eslint/eslint-plugin@19.0.0-alpha.4': + resolution: {integrity: sha512-IhBeiUohYLsnUrSJ92riSrhfIkGefuXIrGTgBnagn887WFR45/Go5dIIivVfMGdvTg849dtLpBDXZrycHY3QFA==} + peerDependencies: + '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + + '@angular-eslint/schematics@19.0.0-alpha.4': + resolution: {integrity: sha512-Xv8g2PbNqhm7igTnY3uuY511Fr+FVRBHG+ZHbcUPZT7YDBaFZRJezAzbbPLZ6GdqqhoQJPHdGuEvo22yKhKXag==} + peerDependencies: + '@angular-devkit/core': '>= 19.0.0 < 20.0.0' + '@angular-devkit/schematics': '>= 19.0.0 < 20.0.0' + + '@angular-eslint/template-parser@18.4.2': + resolution: {integrity: sha512-KGjDLUxMsdjaxC+8VTxCG07Q6qshOTWMYTvp2LZ4QBySDQnQuFwsIJIJfU8jJwzJCkPKfVpnyuHggAn7fdYnxA==} peerDependencies: - '@angular-devkit/core': '>= 18.0.0 < 19.0.0' - '@angular-devkit/schematics': '>= 18.0.0 < 19.0.0' + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' - '@angular-eslint/template-parser@18.4.1': - resolution: {integrity: sha512-LsStXVyso/89gQU5eiJebB/b1j+wrRtTLjk+ODVUTa7NGCCT7B7xI6ToTchkBEpSTHLT9pEQXHsHer3FymsQRQ==} + '@angular-eslint/template-parser@19.0.0-alpha.4': + resolution: {integrity: sha512-Mvy1kbnqoYBQFFpQtmBB/TkhmmoN97ruSv9xa3mpKzv8JlDdVCkIn7IdqLtzcLwGr+MGcPC7GFPl8o7q12N3BQ==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular-eslint/utils@18.4.1': - resolution: {integrity: sha512-F5UGE1J/CRmTbl8vjexQRwRglNqnJwdXCUejaG+qlGssSHoWcRB+ubbR/na3PdnzeJdBE6DkLYElXnOQZ6YKfg==} + '@angular-eslint/utils@18.4.2': + resolution: {integrity: sha512-+c0r33QSkAnGmu/DYAPfzJJk5QDX4TP2d6EFtsenrufqRkZqrOcK4Q5t61J92Ukkr03XoqTzTDSBjlwAfM56Rw==} peerDependencies: '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 eslint: ^8.57.0 || ^9.0.0 typescript: '*' - '@angular/animations@19.0.0-rc.3': - resolution: {integrity: sha512-Ww/BZp/0pFd+pc6tJpQ3wKscY3nJ0UEh60Oe3UScj7KU2h595mIPLSMBzOUKxchZd1CMvYlUC0xO6GnntBF6Xw==} + '@angular-eslint/utils@19.0.0-alpha.4': + resolution: {integrity: sha512-6Pxqs3QqSPBcAkP8I/GYijoPoAmqOYqyQvJGvBWd1oKlA3EqmXSi7uaSUa6nUI6BiA8JEJZTBaSOP6O2oyK25Q==} + peerDependencies: + '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '*' + + '@angular/animations@19.0.1': + resolution: {integrity: sha512-1TZ3meVmoMuQwXaHSCeIGq8tmGcwobCQM2AQ6hfK+j6eyWTSx8BdWWi+Z1iIjiYFx3pJljQiWLAHULZ66Ep/GQ==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/core': 19.0.0-rc.3 + '@angular/core': 19.0.1 - '@angular/build@19.0.0-rc.3': - resolution: {integrity: sha512-SldDMnC5UVbjun3KzATMC3WvOCwOizX2OhRaIbTGouniTpnJwqs7g2kF4BbQAwZN/TomqYHq6rYrDLL0UBp2vA==} + '@angular/build@19.0.2': + resolution: {integrity: sha512-i2mSg9ZoPto3IMNi/HnP2ZOwvcmaPEKrS7EOYeu1m1W9InuZ55ssMqrjKpeohKVYHwep8QmFrmDERbqutaN2hg==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler': ^19.0.0-next.9 - '@angular/compiler-cli': ^19.0.0-next.9 - '@angular/localize': ^19.0.0-next.9 - '@angular/platform-server': ^19.0.0-next.9 - '@angular/service-worker': ^19.0.0-next.9 - '@angular/ssr': ^19.0.0-rc.3 + '@angular/compiler': ^19.0.0 + '@angular/compiler-cli': ^19.0.0 + '@angular/localize': ^19.0.0 + '@angular/platform-server': ^19.0.0 + '@angular/service-worker': ^19.0.0 + '@angular/ssr': ^19.0.2 less: ^4.2.0 postcss: ^8.4.0 tailwindcss: ^2.0.0 || ^3.0.0 @@ -315,96 +344,96 @@ packages: tailwindcss: optional: true - '@angular/cdk@19.0.0-rc.3': - resolution: {integrity: sha512-MlYFgSLMQPU0Y6qteK+zb7e80eNNiBrVGRXcY9xtYbO0yTF+mrE64myCPbemKjyUFADzkmjIQoUCp+imwXelGw==} + '@angular/cdk@19.0.1': + resolution: {integrity: sha512-dIqYBQISvxlpXIU10625rURPjniQV1emXbFF6wAEE48iqx9mm9WZ11KZU4heqA3qp/betZYcVY2Hwc7fLKp4Uw==} peerDependencies: - '@angular/common': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 - '@angular/core': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 + '@angular/common': ^19.0.0 || ^20.0.0 + '@angular/core': ^19.0.0 || ^20.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/cli@19.0.0-rc.3': - resolution: {integrity: sha512-lctAdgs+wmk1TPmd/OaxlgfRKz51kv69FtD+OjWV5YBFX92gOEfbYyE9eIJkFtkbcsUEKGCPii9uomG1texPlQ==} + '@angular/cli@19.0.2': + resolution: {integrity: sha512-TlPrs3hRkHWrQEKwHde9l2F4IgT5tWTx4zFcllzBh2dW9iRpqXSYRb82xNHsbopdAu4lXjsYl7JilV2DQPZEaA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@19.0.0-rc.3': - resolution: {integrity: sha512-5vOFc2cpkb52DVKFTj66Cualf8TtCC7CdAhFQCWAZEIEV0JahGGuuYWKEZVbBkPRZ6jqB/MIGwnfrODCK7bHdA==} + '@angular/common@19.0.1': + resolution: {integrity: sha512-FWAyHlEhPeLHvNLuzSl2rlksK/fVVB5O3soBYOeiKScN1vlAdALbwPDIHhimhNFBV8kmtc144WjkcTxt8MK/4g==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/core': 19.0.0-rc.3 + '@angular/core': 19.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@19.0.0-rc.3': - resolution: {integrity: sha512-eGYx/oYWqVw26WKmZNo3JTY0xleOydx+XMkqJWhQkhQJlrfZHlTXxkRQJHVgzrzhFov1/ERjHYlsIK61qfQ8Bg==} + '@angular/compiler-cli@19.0.1': + resolution: {integrity: sha512-dIpJCRPmmgmPyAqkOwhP4IEj+T5H4s3x39sCCBohqr2mlZcTXp/Fir8CXnMHlzawh4eXm4pvHjvh/bmMH4efrA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} hasBin: true peerDependencies: - '@angular/compiler': 19.0.0-rc.3 + '@angular/compiler': 19.0.1 typescript: '>=5.5 <5.7' - '@angular/compiler@19.0.0-rc.3': - resolution: {integrity: sha512-rzAHs0WyRKGSSo6e2VxAt/1pr/3y6ZBXxo1+wA0yzFKzr4zX7aTc/pEZOZKsrsoZd+GJvMXX21vRt4YNPxKx6Q==} + '@angular/compiler@19.0.1': + resolution: {integrity: sha512-loyI701+As+sWsE4yr9HpIPBqIohpNrGby/hsXtr+zJTMUWp/sKZlavctVtUsWWJhwHMevoybdgd3N9NY97F7g==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/core': 19.0.0-rc.3 + '@angular/core': 19.0.1 peerDependenciesMeta: '@angular/core': optional: true - '@angular/core@19.0.0-rc.3': - resolution: {integrity: sha512-WDAdAwSGsKNCx66NY0t/Ht0bnaSetKhiveaoVIzNhNJ7bYnGrol/yD6XjVEEzbolw/blxRcfas2aLg9bv6TQfw==} + '@angular/core@19.0.1': + resolution: {integrity: sha512-+VpWcg2aC/dY9TM6fsj00enZ6RP5wpRqk/SeRe3UP3Je/n+vWIgHJTb1ZLNeOIvDaE86BhKPMwFS0QVjoEGQFA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 - '@angular/forms@19.0.0-rc.3': - resolution: {integrity: sha512-rY1rTniZXEfTI+TIzw8aISAddTMdLtetiVzKlZmSGHyhik/E8McwQx3Oup2buEeLs2qOSrSkihK6cVwhvqlATQ==} + '@angular/forms@19.0.1': + resolution: {integrity: sha512-PNMQVi97ZK9X7fQeO1li6LxoL9U6v7ByC+4kj7xHAcOGaBCB+EJ/ZPKCKeaGn4G7mJd3iH8SMVzoUQc028KIcw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 19.0.0-rc.3 - '@angular/core': 19.0.0-rc.3 - '@angular/platform-browser': 19.0.0-rc.3 + '@angular/common': 19.0.1 + '@angular/core': 19.0.1 + '@angular/platform-browser': 19.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@angular/material@19.0.0-rc.3': - resolution: {integrity: sha512-RL4o+WJVoaUVmPvPJoQ/Y8nAxopuJ2pTWtr8S1HosmiDIn0xVqqBSK9KWHtvHZ41nsXlH9r/4MeASOc0x0mnyQ==} + '@angular/material@19.0.1': + resolution: {integrity: sha512-pAZ+cgBUAJjXmwAY4u1NXuxcxJKHts0s7ZNpf6JGUu+yWArLOc/BwFTDO9Htzz2E82eMH417d1ny4fpYwdgIZg==} peerDependencies: - '@angular/animations': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 - '@angular/cdk': 19.0.0-rc.3 - '@angular/common': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 - '@angular/core': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 - '@angular/forms': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 - '@angular/platform-browser': ^19.0.0-0 || ^19.1.0-0 || ^19.2.0-0 || ^19.3.0-0 || ^20.0.0-0 + '@angular/animations': ^19.0.0 || ^20.0.0 + '@angular/cdk': 19.0.1 + '@angular/common': ^19.0.0 || ^20.0.0 + '@angular/core': ^19.0.0 || ^20.0.0 + '@angular/forms': ^19.0.0 || ^20.0.0 + '@angular/platform-browser': ^19.0.0 || ^20.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/platform-browser-dynamic@19.0.0-rc.3': - resolution: {integrity: sha512-SUwQV6SHSfC1HdQGj/LzwW7qDmWZejlb22Ox+ECFZ+c8dEZj1blPdugOaLQkY+1Tnzt46zteNQruygVbckqZTg==} + '@angular/platform-browser-dynamic@19.0.1': + resolution: {integrity: sha512-A8sM0NTwZPFpv5kWSUeRhMENCw8kmBxR9CX9TMVeU6u9TP+IT3SFhUWhDQZNbmJAHhyAuk5B1gBJ/aoz0/OBcw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 19.0.0-rc.3 - '@angular/compiler': 19.0.0-rc.3 - '@angular/core': 19.0.0-rc.3 - '@angular/platform-browser': 19.0.0-rc.3 + '@angular/common': 19.0.1 + '@angular/compiler': 19.0.1 + '@angular/core': 19.0.1 + '@angular/platform-browser': 19.0.1 - '@angular/platform-browser@19.0.0-rc.3': - resolution: {integrity: sha512-hPf/wdF9GccBSC5AfG7PXuzb8oG1YUIT16xdU9zekCLnCi6uWKuYzVMRnbXWKSWNNumK4SHI2sAB/vzH9XEzTA==} + '@angular/platform-browser@19.0.1': + resolution: {integrity: sha512-ycl6GsK5avKz2PKyKR8G3eqH5rWdzTqRfYStN+1Ufhopx9jmCQ9r0JSIekoHJ8W2KDZfojWp6f4izDMvKnUpvA==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/animations': 19.0.0-rc.3 - '@angular/common': 19.0.0-rc.3 - '@angular/core': 19.0.0-rc.3 + '@angular/animations': 19.0.1 + '@angular/common': 19.0.1 + '@angular/core': 19.0.1 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@19.0.0-rc.3': - resolution: {integrity: sha512-6OQ0440oa9CxB6v8YntQSAG5ap/cB19lY+KnWn78LVRJtOOfEIDB+xRm4/EtQiSUSdJjl+CkqVMRgxt/1xQ1uw==} + '@angular/router@19.0.1': + resolution: {integrity: sha512-/9f7RxVqOTASFhpqla7x9V58SE8Yv4SClKRikvv5Tn5EGDbSVR3DgGu6qENP57A2pVPW4Ho5er5KKT35HjhcFw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/common': 19.0.0-rc.3 - '@angular/core': 19.0.0-rc.3 - '@angular/platform-browser': 19.0.0-rc.3 + '@angular/common': 19.0.1 + '@angular/core': 19.0.1 + '@angular/platform-browser': 19.0.1 rxjs: ^6.5.3 || ^7.4.0 '@babel/code-frame@7.26.2': @@ -1600,11 +1629,11 @@ packages: resolution: {integrity: sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==} engines: {node: '>= 10'} - '@ngtools/webpack@19.0.0-rc.3': - resolution: {integrity: sha512-2QmiIZ+VOQOkUUdIs5nawAgMIhMnVq0bMlhwqFPzmhhcJaDCWZtla8F1XmEU6x5YYDKInhs510iGUTo5jUmVag==} + '@ngtools/webpack@19.0.2': + resolution: {integrity: sha512-wHAIItix6zAOczdLjY9Z/e4mtpBDSzBkN//N6GHoGtjtCSzqZg4uPg5KG7B5tpVb/u6IMRK+4hhu9Vu8lhzz8g==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler-cli': ^19.0.0-next.0 + '@angular/compiler-cli': ^19.0.0 typescript: '>=5.5 <5.7' webpack: ^5.54.0 @@ -1849,8 +1878,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@19.0.0-rc.3': - resolution: {integrity: sha512-JAWtT5fGRHo94tq+tt3hNhGupefKAP6tyEVOCXvRo0yZ/2kZ3JvHxTDYrWdPoGswZWkxYLevDattMhiUJZ0VWg==} + '@schematics/angular@19.0.2': + resolution: {integrity: sha512-KPNKJRcuJ9kWctcW+g7WzmCEHpjNnYbNVyiU/MvKdQX0uhGXnXE13YMVfgYIf/0KeHcVp5dkAwg5dkmm9PGNTw==} engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@3.0.0': @@ -1888,8 +1917,12 @@ packages: resolution: {integrity: sha512-bZY04sWSn2YWAqcuY/fYy03ynARYHwn8xzYgdqqcHBXsBXhOc+bbWwHyLwW28XAA2NjzjMPZZAM3N5D09i+zEQ==} engines: {node: '>=16', pnpm: '>=8.6.0'} - '@thednp/shorty@2.0.7': - resolution: {integrity: sha512-PQ388ZznrgnkikwkDCqqFfkGAYWXS2ijFmXD63Ej47Md6VrV5WJqhgQilhu3tSkzddtbDJlz4tQTj4RYVrWUoA==} + '@thednp/position-observer@1.0.5': + resolution: {integrity: sha512-3ceT4kl8okq4HMMqQ3+7UGosf6u5p6n95kvYUqtgJGTSE69LcU4ep5NjeSChEmvk/I9V/3tNMMlXYKCSWYq61Q==} + engines: {node: '>=16', pnpm: '>=8.6.0'} + + '@thednp/shorty@2.0.9': + resolution: {integrity: sha512-btulzTMSxSQxNipN5iLMtw6MaTrZ3P0feRUX/rl2fZ1m31RI56PC967lnN5bwUCpZLzry0pl9QoCKlT3GtU+mw==} engines: {node: '>=16', pnpm: '>=8.6.0'} '@ts-morph/common@0.25.0': @@ -1936,8 +1969,8 @@ packages: '@types/express-serve-static-core@4.19.6': resolution: {integrity: sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==} - '@types/express-serve-static-core@5.0.1': - resolution: {integrity: sha512-CRICJIl0N5cXDONAdlTv5ShATZ4HEwk6kDDIW2/w9qOWKg+NU/5F8wYRWCrONad0/UKkloNSmmyN/wX4rtpbVA==} + '@types/express-serve-static-core@5.0.2': + resolution: {integrity: sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==} '@types/express@4.17.21': resolution: {integrity: sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==} @@ -1966,8 +1999,8 @@ packages: '@types/node-forge@1.3.11': resolution: {integrity: sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==} - '@types/node@22.9.0': - resolution: {integrity: sha512-vuyHg81vvWA1Z1ELfvLko2c8f34gyA0zaic0+Rllc5lbCnbSyuvb2Oxpm6TAUAC/2xZN3QGqxBNggD1nNR2AfQ==} + '@types/node@22.10.0': + resolution: {integrity: sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==} '@types/qs@6.9.17': resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} @@ -1996,8 +2029,8 @@ packages: '@types/ws@8.5.13': resolution: {integrity: sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==} - '@typescript-eslint/eslint-plugin@8.15.0': - resolution: {integrity: sha512-+zkm9AR1Ds9uLWN3fkoeXgFppaQ+uEVtfOV62dDmsy9QCNqlRHWNEck4yarvRNrvRcHQLGfqBNui3cimoz8XAg==} + '@typescript-eslint/eslint-plugin@8.16.0': + resolution: {integrity: sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.0.0 || ^8.0.0-alpha.0 @@ -2007,8 +2040,8 @@ packages: typescript: optional: true - '@typescript-eslint/parser@8.15.0': - resolution: {integrity: sha512-7n59qFpghG4uazrF9qtGKBZXn7Oz4sOMm8dwNWDQY96Xlm2oX67eipqcblDj+oY1lLCbf1oltMZFpUso66Kl1A==} + '@typescript-eslint/parser@8.16.0': + resolution: {integrity: sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2017,12 +2050,12 @@ packages: typescript: optional: true - '@typescript-eslint/scope-manager@8.15.0': - resolution: {integrity: sha512-QRGy8ADi4J7ii95xz4UoiymmmMd/zuy9azCaamnZ3FM8T5fZcex8UfJcjkiEZjJSztKfEBe3dZ5T/5RHAmw2mA==} + '@typescript-eslint/scope-manager@8.16.0': + resolution: {integrity: sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/type-utils@8.15.0': - resolution: {integrity: sha512-UU6uwXDoI3JGSXmcdnP5d8Fffa2KayOhUUqr/AiBnG1Gl7+7ut/oyagVeSkh7bxQ0zSXV9ptRh/4N15nkCqnpw==} + '@typescript-eslint/type-utils@8.16.0': + resolution: {integrity: sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2031,12 +2064,12 @@ packages: typescript: optional: true - '@typescript-eslint/types@8.15.0': - resolution: {integrity: sha512-n3Gt8Y/KyJNe0S3yDCD2RVKrHBC4gTUcLTebVBXacPy091E6tNspFLKRXlk3hwT4G55nfr1n2AdFqi/XMxzmPQ==} + '@typescript-eslint/types@8.16.0': + resolution: {integrity: sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.15.0': - resolution: {integrity: sha512-1eMp2JgNec/niZsR7ioFBlsh/Fk0oJbhaqO0jRyQBMgkz7RrFfkqF9lYYmBoGBaSiLnu8TAPQTwoTUiSTUW9dg==} + '@typescript-eslint/typescript-estree@8.16.0': + resolution: {integrity: sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '*' @@ -2044,8 +2077,8 @@ packages: typescript: optional: true - '@typescript-eslint/utils@8.15.0': - resolution: {integrity: sha512-k82RI9yGhr0QM3Dnq+egEpz9qB6Un+WLYhmoNcvl8ltMEededhh7otBVVIDDsEEttauwdY/hQoSsOv13lxrFzQ==} + '@typescript-eslint/utils@8.16.0': + resolution: {integrity: sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -2054,8 +2087,8 @@ packages: typescript: optional: true - '@typescript-eslint/visitor-keys@8.15.0': - resolution: {integrity: sha512-h8vYOulWec9LhpwfAdZf2bjr8xIp0KNKnpgqSz0qqYYKAW/QZKw3ktRndbiAtUz4acH4QLQavwZBYCc0wulA/Q==} + '@typescript-eslint/visitor-keys@8.16.0': + resolution: {integrity: sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@vitejs/plugin-basic-ssl@1.1.0': @@ -2180,13 +2213,18 @@ packages: ajv@8.17.1: resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - angular-eslint@18.4.1: - resolution: {integrity: sha512-tRy0SeWC2zoftEYTlUU6WLtzyxF7cjlodnnG40EO2PGPwRN2m+EWQn7de0RZz0MIYPl36px8gj9CztiHO2risA==} + angular-eslint@19.0.0-alpha.4: + resolution: {integrity: sha512-/7Jl/rCoflc5wkjkVCBIs1DDwr/vVJ8Rp4LgCofoIPSbAlrZ8SkS8oLoLTIbvhrM6kO9Gq5LnzL13Y//hNndsw==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: '*' typescript-eslint: ^8.0.0 + angularx-qrcode@19.0.0: + resolution: {integrity: sha512-uH1gO/X1hgSojZwgO3EmaXP+MvWCgZm5WGh3y1ZL2+VMstEGEMtJGZTyR645fB7ABF2ZIBUMB9h/SKvGJQX/zQ==} + peerDependencies: + '@angular/core': ^19.0.0 + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -2328,8 +2366,8 @@ packages: boolbase@1.0.0: resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} - bootstrap.native@5.0.13: - resolution: {integrity: sha512-SiiTxaK3LjuOjPaXEnDBQNY3w0t28Qdx6I8drortuFg6Ch3q6cWoOxlFHThcGOPewziVarQAA4WPE00GFQmbWQ==} + bootstrap.native@5.1.1: + resolution: {integrity: sha512-QlFMbaCSo1AvfoiiI3jdJk8Rh3BZIXh7UjVcR9/wo+aVTZqOrCrePA4f2chpXLZxwbHitWyMgl1CJSSSqqx8/w==} engines: {node: '>=16', pnpm: '>=8.6.0'} brace-expansion@1.1.11: @@ -2384,8 +2422,12 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001680: - resolution: {integrity: sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==} + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001684: + resolution: {integrity: sha512-G1LRwLIQjBQoyq0ZJGqGIJUXzJ8irpbjHLpVRXDvBEScFJ9b17sgK6vlx0GAJFE21okD7zXl08rRRUfq6HdoEQ==} ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} @@ -2451,6 +2493,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} @@ -2640,6 +2685,10 @@ packages: decache@4.6.2: resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} @@ -2706,6 +2755,9 @@ packages: didyoumean@1.2.2: resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -2741,8 +2793,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.63: - resolution: {integrity: sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==} + electron-to-chromium@1.5.65: + resolution: {integrity: sha512-PWVzBjghx7/wop6n22vS2MLU8tKGd4Q91aCEGhG/TYmW6PP5OcSXcdnxTe1NNt0T66N8D6jxh4kC8UsdzOGaIw==} emitter-component@1.1.2: resolution: {integrity: sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==} @@ -2905,8 +2957,8 @@ packages: peerDependencies: eslint: '>=6.0.0' - eslint-plugin-jsdoc@50.5.0: - resolution: {integrity: sha512-xTkshfZrUbiSHXBwZ/9d5ulZ2OcHXxSvm/NPo494H/hadLRJwOq5PMV0EUpMqsb9V+kQo+9BAgi6Z7aJtdBp2A==} + eslint-plugin-jsdoc@50.6.0: + resolution: {integrity: sha512-tCNp4fR79Le3dYTPB0dKEv7yFyvGkUCa+Z3yuTrrNGGOxBlXo9Pn0PEgroOZikUQOGjxoGMVKNjrOHcYEdfszg==} engines: {node: '>=18'} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 @@ -3068,6 +3120,10 @@ packages: resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==} engines: {node: '>=14.16'} + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -3240,8 +3296,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hosted-git-info@8.0.0: - resolution: {integrity: sha512-4nw3vOVR+vHUOT8+U4giwe2tcGv+R3pwwRidUe67DoMBTjhrfr6rZYJVVwdkBE+Um050SG+X9tf0Jo4fOpn01w==} + hosted-git-info@8.0.2: + resolution: {integrity: sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg==} engines: {node: ^18.17.0 || >=20.5.0} hpack.js@2.1.6: @@ -3322,8 +3378,8 @@ packages: resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} engines: {node: '>=10.18'} - i18next@23.16.5: - resolution: {integrity: sha512-KTlhE3EP9x6pPTAW7dy0WKIhoCpfOGhRQlO+jttQLgzVaoOjWwBWramu7Pp0i+8wDNduuzXfe3kkVbzrKyrbTA==} + i18next@23.16.8: + resolution: {integrity: sha512-06r/TitrM88Mg5FdUXAKL96dJMzgqLE5dv3ryBAra4KCwD9mJ4ndOTS95ZuymIGoE+2hzfdaMak2X11/es7ZWg==} iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} @@ -3359,8 +3415,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - immutable@5.0.2: - resolution: {integrity: sha512-1NU7hWZDkV7hJ4PJ9dur9gTNQ4ePNPN4k9/0YhwjzykTi/+3Q5pF93YU5QoVj8BuOnhLgaY8gs0U2pj4kSYVcw==} + immutable@5.0.3: + resolution: {integrity: sha512-P8IdPQHq3lA1xVeBRi5VPqUm5HDgKnx0Ru51wZz5mjxHr5n3RWhjIpOFU7ybkUxfB+5IToy+OLaHYDBIWsv+uw==} import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} @@ -3407,8 +3463,8 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-bun-module@1.2.1: - resolution: {integrity: sha512-AmidtEM6D6NmUiLOvvU7+IePxjEjOzra2h0pSrsfSAcXwl/83zLLXDByafUJy9k/rKK0pvXMLdwKwGHlX2Ke6Q==} + is-bun-module@1.3.0: + resolution: {integrity: sha512-DgXeu5UWI0IsMQundYb5UAOzm6G2eVnarJ0byP6Tm55iZNKceD59LNPA2L4VvsScTtHcw0yEkVwSf7PC+QoLSA==} is-core-module@2.15.1: resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} @@ -3614,6 +3670,10 @@ packages: resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==} engines: {'0': node >= 0.2.0} + jwt-decode@4.0.0: + resolution: {integrity: sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==} + engines: {node: '>=18'} + karma-chrome-launcher@3.2.0: resolution: {integrity: sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q==} @@ -3716,6 +3776,10 @@ packages: resolution: {integrity: sha512-FMJTLMXfCLMLfJxcX9PFqX5qD88Z5MRGaZCVzfuqeZSPsyiBzs+pahDQjbIWz2QIzPZz0NX9Zy4FX3lmK6YHIg==} engines: {node: '>= 12.13.0'} + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -3932,8 +3996,8 @@ packages: micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} - micromark-util-subtokenize@2.0.2: - resolution: {integrity: sha512-xKxhkB62vwHUuuxHe9Xqty3UaAsizV2YKq5OV344u3hFBbf8zIYrhYOWhAQb94MtMPkjTOzzjJ/hid9/dR5vFA==} + micromark-util-subtokenize@2.0.3: + resolution: {integrity: sha512-VXJJuNxYWSoYL6AJ6OQECCFGhIU2GGHMw8tahogePBrjkG8aCCas3ibkp7RnVOSTClg2is05/R7maAhF1XyQMg==} micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} @@ -4098,8 +4162,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.7: - resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -4173,8 +4237,8 @@ packages: resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==} engines: {node: ^18.17.0 || >=20.5.0} - npm-install-checks@7.1.0: - resolution: {integrity: sha512-bkTildVlofeMX7wiOaWk3PlW7YcBXAuEc7TWpOxwUgalG5ZvgT/ms+6OX9zt7iGLv4+VhKbRZhpOfgQJzk1YAw==} + npm-install-checks@7.1.1: + resolution: {integrity: sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==} engines: {node: ^18.17.0 || >=20.5.0} npm-normalize-package-bin@4.0.0: @@ -4219,6 +4283,10 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} + oidc-client-ts@3.1.0: + resolution: {integrity: sha512-IDopEXjiwjkmJLYZo6BTlvwOtnlSniWZkKZoXforC/oLZHC9wkIxd25Kwtmo5yKFMMVcsp3JY6bhcNJqdYk8+g==} + engines: {node: '>=18'} + on-finished@2.3.0: resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} engines: {node: '>= 0.8'} @@ -4273,6 +4341,10 @@ packages: resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} engines: {node: '>=0.10.0'} + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} @@ -4281,6 +4353,10 @@ packages: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} @@ -4301,6 +4377,10 @@ packages: resolution: {integrity: sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==} engines: {node: '>=16.17'} + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -4411,6 +4491,10 @@ packages: resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==} engines: {node: '>=14.16'} + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + postcss-import@15.1.0: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} @@ -4504,8 +4588,8 @@ packages: resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} engines: {node: '>=6.0.0'} - prettier-plugin-tailwindcss@0.6.8: - resolution: {integrity: sha512-dGu3kdm7SXPkiW4nzeWKCl3uoImdd5CTZEJGxyypEPL37Wj0HT2pLqjrvSei1nTeuQfO4PUfjeW5cTUNRLZ4sA==} + prettier-plugin-tailwindcss@0.6.9: + resolution: {integrity: sha512-r0i3uhaZAXYP0At5xGfJH876W3HHGHDp+LCRUJrs57PBeQ6mYHMwr25KH8NPX44F2yGTvdnH7OqCshlQx183Eg==} engines: {node: '>=14.21.3'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -4619,6 +4703,11 @@ packages: resolution: {integrity: sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg==} engines: {node: '>=0.9'} + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + qs@6.13.0: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} @@ -4674,15 +4763,15 @@ packages: regex-parser@2.3.0: resolution: {integrity: sha512-TVILVSz2jY5D47F4mA4MppkBrafEaiUWJO/TcZHEIuI13AqoZMkK1WMA4Om1YkYbTx+9Ki1/tSUXbceyr9saRg==} - regexpu-core@6.1.1: - resolution: {integrity: sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==} + regexpu-core@6.2.0: + resolution: {integrity: sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==} engines: {node: '>=4'} regjsgen@0.8.0: resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==} - regjsparser@0.11.2: - resolution: {integrity: sha512-3OGZZ4HoLJkkAZx/48mTXJNlmqTGOzc0o9OWQPuWpkOlXXPbyN6OafCcoXUnBqE2D3f/T5L+pWc1kdEmnfnRsA==} + regjsparser@0.12.0: + resolution: {integrity: sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==} hasBin: true require-directory@2.1.1: @@ -4693,6 +4782,9 @@ packages: resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -4843,6 +4935,9 @@ packages: resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} engines: {node: '>= 0.8.0'} + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -5148,8 +5243,8 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-api-utils@1.4.0: - resolution: {integrity: sha512-032cPxaEKwM+GT3vA5JXNzIaizx388rhsSW79vGRNGXfRRAdEAn2mvk36PvK5HnOchyWZ7afLEXqYCvPCrzuzQ==} + ts-api-utils@1.4.2: + resolution: {integrity: sha512-ZF5gQIQa/UmzfvxbHZI3JXN0/Jt+vnAfAviNRAMc491laiK6YCLpCW9ft8oaCRFOTxCZtUTE6XB0ZQAe3olntw==} engines: {node: '>=16'} peerDependencies: typescript: '>=4.2.0' @@ -5182,8 +5277,8 @@ packages: typed-assert@1.0.9: resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} - typescript-eslint@8.15.0: - resolution: {integrity: sha512-wY4FRGl0ZI+ZU4Jo/yjdBu0lVTSML58pu6PgGtJmCufvzfV565pUF6iACQt092uFOd49iLOTX/sEVmHtbSrS+w==} + typescript-eslint@8.16.0: + resolution: {integrity: sha512-wDkVmlY6O2do4V+lZd0GtRfbtXbeD0q9WygwXXSJnC1xorE8eqyC2L1tJimqpSeFrOzRlYtWnUp/uzgHQOgfBQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 @@ -5206,8 +5301,8 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - undici-types@6.19.8: - resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} @@ -5417,6 +5512,9 @@ packages: resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} engines: {node: '>=0.8.0'} + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + which@1.3.1: resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} hasBin: true @@ -5493,6 +5591,9 @@ packages: utf-8-validate: optional: true + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -5507,11 +5608,15 @@ packages: resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==} engines: {node: '>=18'} - yaml@2.6.0: - resolution: {integrity: sha512-a6ae//JvKDEra2kdi1qzCyrJW/WZCgFi8ydDV+eXExl95t+5R+ijnqHJbz9tmMh8FUjx3iv2fCQ4dclAQlO2UQ==} + yaml@2.6.1: + resolution: {integrity: sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==} engines: {node: '>= 14'} hasBin: true + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} @@ -5520,6 +5625,10 @@ packages: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + yargs@16.2.0: resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} engines: {node: '>=10'} @@ -5557,21 +5666,21 @@ snapshots: '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 - '@angular-devkit/architect@0.1900.0-rc.3(chokidar@4.0.1)': + '@angular-devkit/architect@0.1900.2(chokidar@4.0.1)': dependencies: - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) rxjs: 7.8.1 transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.9.0)(chokidar@4.0.1)(karma@6.4.4)(tailwindcss@3.4.15)(typescript@5.6.3)(vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': + '@angular-devkit/build-angular@19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.0)(chokidar@4.0.1)(karma@6.4.4)(tailwindcss@3.4.15)(typescript@5.6.3)(vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.1900.0-rc.3(chokidar@4.0.1) - '@angular-devkit/build-webpack': 0.1900.0-rc.3(chokidar@4.0.1)(webpack-dev-server@5.1.0(webpack@5.96.1))(webpack@5.96.1(esbuild@0.24.0)) - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) - '@angular/build': 19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.9.0)(chokidar@4.0.1)(less@4.2.0)(postcss@8.4.49)(tailwindcss@3.4.15)(terser@5.36.0)(typescript@5.6.3) - '@angular/compiler-cli': 19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) + '@angular-devkit/architect': 0.1900.2(chokidar@4.0.1) + '@angular-devkit/build-webpack': 0.1900.2(chokidar@4.0.1)(webpack-dev-server@5.1.0(webpack@5.96.1))(webpack@5.96.1(esbuild@0.24.0)) + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) + '@angular/build': 19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.0)(chokidar@4.0.1)(less@4.2.0)(postcss@8.4.49)(tailwindcss@3.4.15)(terser@5.36.0)(typescript@5.6.3) + '@angular/compiler-cli': 19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) '@babel/core': 7.26.0 '@babel/generator': 7.26.2 '@babel/helper-annotate-as-pure': 7.25.9 @@ -5582,8 +5691,8 @@ snapshots: '@babel/preset-env': 7.26.0(@babel/core@7.26.0) '@babel/runtime': 7.26.0 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(typescript@5.6.3)(webpack@5.96.1(esbuild@0.24.0)) - '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + '@ngtools/webpack': 19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(typescript@5.6.3)(webpack@5.96.1(esbuild@0.24.0)) + '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.20(postcss@8.4.49) babel-loader: 9.2.1(@babel/core@7.26.0)(webpack@5.96.1(esbuild@0.24.0)) @@ -5647,9 +5756,9 @@ snapshots: - vite - webpack-cli - '@angular-devkit/build-webpack@0.1900.0-rc.3(chokidar@4.0.1)(webpack-dev-server@5.1.0(webpack@5.96.1))(webpack@5.96.1(esbuild@0.24.0))': + '@angular-devkit/build-webpack@0.1900.2(chokidar@4.0.1)(webpack-dev-server@5.1.0(webpack@5.96.1))(webpack@5.96.1(esbuild@0.24.0))': dependencies: - '@angular-devkit/architect': 0.1900.0-rc.3(chokidar@4.0.1) + '@angular-devkit/architect': 0.1900.2(chokidar@4.0.1) rxjs: 7.8.1 webpack: 5.96.1(esbuild@0.24.0) webpack-dev-server: 5.1.0(webpack@5.96.1) @@ -5667,7 +5776,7 @@ snapshots: optionalDependencies: chokidar: 4.0.1 - '@angular-devkit/core@19.0.0-rc.3(chokidar@4.0.1)': + '@angular-devkit/core@19.0.2(chokidar@4.0.1)': dependencies: ajv: 8.17.1 ajv-formats: 3.0.1(ajv@8.17.1) @@ -5688,9 +5797,9 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/schematics@19.0.0-rc.3(chokidar@4.0.1)': + '@angular-devkit/schematics@19.0.2(chokidar@4.0.1)': dependencies: - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) jsonc-parser: 3.3.1 magic-string: 0.30.12 ora: 5.4.1 @@ -5698,38 +5807,48 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-eslint/builder@18.4.1(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/builder@19.0.0-alpha.4(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: eslint: 9.15.0(jiti@1.21.6) typescript: 5.6.3 - '@angular-eslint/bundled-angular-compiler@18.4.1': {} + '@angular-eslint/bundled-angular-compiler@18.4.2': {} + + '@angular-eslint/bundled-angular-compiler@19.0.0-alpha.4': {} - '@angular-eslint/eslint-plugin-template@18.4.1(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/eslint-plugin-template@19.0.0-alpha.4(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 18.4.1 - '@angular-eslint/utils': 18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/bundled-angular-compiler': 19.0.0-alpha.4 + '@angular-eslint/utils': 19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) aria-query: 5.3.2 axobject-query: 4.1.0 eslint: 9.15.0(jiti@1.21.6) typescript: 5.6.3 - '@angular-eslint/eslint-plugin@18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/eslint-plugin@18.4.2(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 18.4.1 - '@angular-eslint/utils': 18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/bundled-angular-compiler': 18.4.2 + '@angular-eslint/utils': 18.4.2(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) typescript: 5.6.3 - '@angular-eslint/schematics@18.4.1(@angular-devkit/core@19.0.0-rc.3(chokidar@4.0.1))(@angular-devkit/schematics@19.0.0-rc.3(chokidar@4.0.1))(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/eslint-plugin@19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) - '@angular-devkit/schematics': 19.0.0-rc.3(chokidar@4.0.1) - '@angular-eslint/eslint-plugin': 18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@angular-eslint/eslint-plugin-template': 18.4.1(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/bundled-angular-compiler': 19.0.0-alpha.4 + '@angular-eslint/utils': 19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + eslint: 9.15.0(jiti@1.21.6) + typescript: 5.6.3 + + '@angular-eslint/schematics@19.0.0-alpha.4(@angular-devkit/core@19.0.2(chokidar@4.0.1))(@angular-devkit/schematics@19.0.2(chokidar@4.0.1))(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + dependencies: + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) + '@angular-devkit/schematics': 19.0.2(chokidar@4.0.1) + '@angular-eslint/eslint-plugin': 19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/eslint-plugin-template': 19.0.0-alpha.4(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) ignore: 6.0.2 semver: 7.6.3 strip-json-comments: 3.1.1 @@ -5739,37 +5858,51 @@ snapshots: - eslint - typescript - '@angular-eslint/template-parser@18.4.1(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/template-parser@18.4.2(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + dependencies: + '@angular-eslint/bundled-angular-compiler': 18.4.2 + eslint: 9.15.0(jiti@1.21.6) + eslint-scope: 8.2.0 + typescript: 5.6.3 + + '@angular-eslint/template-parser@19.0.0-alpha.4(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 18.4.1 + '@angular-eslint/bundled-angular-compiler': 19.0.0-alpha.4 eslint: 9.15.0(jiti@1.21.6) eslint-scope: 8.2.0 typescript: 5.6.3 - '@angular-eslint/utils@18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@angular-eslint/utils@18.4.2(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 18.4.1 - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/bundled-angular-compiler': 18.4.2 + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) typescript: 5.6.3 - '@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))': + '@angular-eslint/utils@19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + '@angular-eslint/bundled-angular-compiler': 19.0.0-alpha.4 + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + eslint: 9.15.0(jiti@1.21.6) + typescript: 5.6.3 + + '@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))': + dependencies: + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) tslib: 2.8.1 - '@angular/build@19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.9.0)(chokidar@4.0.1)(less@4.2.0)(postcss@8.4.49)(tailwindcss@3.4.15)(terser@5.36.0)(typescript@5.6.3)': + '@angular/build@19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@types/node@22.10.0)(chokidar@4.0.1)(less@4.2.0)(postcss@8.4.49)(tailwindcss@3.4.15)(terser@5.36.0)(typescript@5.6.3)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.1900.0-rc.3(chokidar@4.0.1) - '@angular/compiler': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) - '@angular/compiler-cli': 19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) + '@angular-devkit/architect': 0.1900.2(chokidar@4.0.1) + '@angular/compiler': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/compiler-cli': 19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 '@babel/helper-split-export-declaration': 7.24.7 '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.0) - '@inquirer/confirm': 5.0.2(@types/node@22.9.0) - '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) + '@inquirer/confirm': 5.0.2(@types/node@22.10.0) + '@vitejs/plugin-basic-ssl': 1.1.0(vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0)) beasties: 0.1.0 browserslist: 4.24.2 esbuild: 0.24.0 @@ -5786,7 +5919,7 @@ snapshots: sass: 1.80.7 semver: 7.6.3 typescript: 5.6.3 - vite: 5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + vite: 5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) watchpack: 2.4.2 optionalDependencies: less: 4.2.0 @@ -5803,23 +5936,23 @@ snapshots: - supports-color - terser - '@angular/cdk@19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1)': + '@angular/cdk@19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1)': dependencies: - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) rxjs: 7.8.1 tslib: 2.8.1 optionalDependencies: parse5: 7.2.1 - '@angular/cli@19.0.0-rc.3(@types/node@22.9.0)(chokidar@4.0.1)': + '@angular/cli@19.0.2(@types/node@22.10.0)(chokidar@4.0.1)': dependencies: - '@angular-devkit/architect': 0.1900.0-rc.3(chokidar@4.0.1) - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) - '@angular-devkit/schematics': 19.0.0-rc.3(chokidar@4.0.1) - '@inquirer/prompts': 7.1.0(@types/node@22.9.0) - '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.1.0(@types/node@22.9.0)) - '@schematics/angular': 19.0.0-rc.3(chokidar@4.0.1) + '@angular-devkit/architect': 0.1900.2(chokidar@4.0.1) + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) + '@angular-devkit/schematics': 19.0.2(chokidar@4.0.1) + '@inquirer/prompts': 7.1.0(@types/node@22.10.0) + '@listr2/prompt-adapter-inquirer': 2.0.18(@inquirer/prompts@7.1.0(@types/node@22.10.0)) + '@schematics/angular': 19.0.2(chokidar@4.0.1) '@yarnpkg/lockfile': 1.1.0 ini: 5.0.0 jsonc-parser: 3.3.1 @@ -5837,15 +5970,15 @@ snapshots: - chokidar - supports-color - '@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1)': + '@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1)': dependencies: - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) rxjs: 7.8.1 tslib: 2.8.1 - '@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3)': + '@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3)': dependencies: - '@angular/compiler': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/compiler': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) '@babel/core': 7.26.0 '@jridgewell/sourcemap-codec': 1.5.0 chokidar: 4.0.1 @@ -5858,58 +5991,58 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))': + '@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))': dependencies: tslib: 2.8.1 optionalDependencies: - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) - '@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)': + '@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)': dependencies: rxjs: 7.8.1 tslib: 2.8.1 zone.js: 0.15.0 - '@angular/forms@19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1)': + '@angular/forms@19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1)': dependencies: - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) - '@angular/platform-browser': 19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/platform-browser': 19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) rxjs: 7.8.1 tslib: 2.8.1 - '@angular/material@19.0.0-rc.3(2ggkf5bmbgan54h663qv7i4v7a)': + '@angular/material@19.0.1(caeb6eiffeog75fr4ymtnaa34q)': dependencies: - '@angular/animations': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) - '@angular/cdk': 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) - '@angular/forms': 19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) - '@angular/platform-browser': 19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/animations': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/cdk': 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/forms': 19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1) + '@angular/platform-browser': 19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) rxjs: 7.8.1 tslib: 2.8.1 - '@angular/platform-browser-dynamic@19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))': + '@angular/platform-browser-dynamic@19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))': dependencies: - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/compiler': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) - '@angular/platform-browser': 19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/compiler': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/platform-browser': 19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) tslib: 2.8.1 - '@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))': + '@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))': dependencies: - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/animations': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) - '@angular/router@19.0.0-rc.3(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1)': + '@angular/router@19.0.1(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(@angular/platform-browser@19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(rxjs@7.8.1)': dependencies: - '@angular/common': 19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) - '@angular/core': 19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0) - '@angular/platform-browser': 19.0.0-rc.3(@angular/animations@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)) + '@angular/common': 19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1) + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) + '@angular/platform-browser': 19.0.1(@angular/animations@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(@angular/common@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0))(rxjs@7.8.1))(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)) rxjs: 7.8.1 tslib: 2.8.1 @@ -6018,14 +6151,14 @@ snapshots: dependencies: '@babel/core': 7.25.8 '@babel/helper-annotate-as-pure': 7.25.9 - regexpu-core: 6.1.1 + regexpu-core: 6.2.0 semver: 6.3.1 '@babel/helper-create-regexp-features-plugin@7.25.9(@babel/core@7.26.0)': dependencies: '@babel/core': 7.26.0 '@babel/helper-annotate-as-pure': 7.25.9 - regexpu-core: 6.1.1 + regexpu-core: 6.2.0 semver: 6.3.1 '@babel/helper-define-polyfill-provider@0.6.3(@babel/core@7.25.8)': @@ -7127,7 +7260,7 @@ snapshots: '@babel/preset-env': 7.25.8(@babel/core@7.25.8) '@compodoc/live-server': 1.2.3 '@compodoc/ngd-transformer': 2.1.3 - bootstrap.native: 5.0.13 + bootstrap.native: 5.1.1 cheerio: 1.0.0-rc.12 chokidar: 4.0.1 colors: 1.4.0 @@ -7141,7 +7274,7 @@ snapshots: glob: 11.0.0 handlebars: 4.7.8 html-entities: 2.5.2 - i18next: 23.16.5 + i18next: 23.16.8 json5: 2.2.3 lodash: 4.17.21 loglevel: 1.9.2 @@ -7406,25 +7539,25 @@ snapshots: '@humanwhocodes/retry@0.4.1': {} - '@inquirer/checkbox@4.0.2(@types/node@22.9.0)': + '@inquirer/checkbox@4.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) + '@inquirer/core': 10.1.0(@types/node@22.10.0) '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 - '@inquirer/confirm@5.0.2(@types/node@22.9.0)': + '@inquirer/confirm@5.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 - '@inquirer/core@10.1.0(@types/node@22.9.0)': + '@inquirer/core@10.1.0(@types/node@22.10.0)': dependencies: '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.9.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) ansi-escapes: 4.3.2 cli-width: 4.1.0 mute-stream: 2.0.0 @@ -7435,76 +7568,76 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@inquirer/editor@4.1.0(@types/node@22.9.0)': + '@inquirer/editor@4.1.0(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 external-editor: 3.1.0 - '@inquirer/expand@4.0.2(@types/node@22.9.0)': + '@inquirer/expand@4.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 yoctocolors-cjs: 2.1.2 '@inquirer/figures@1.0.8': {} - '@inquirer/input@4.0.2(@types/node@22.9.0)': + '@inquirer/input@4.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 - '@inquirer/number@3.0.2(@types/node@22.9.0)': + '@inquirer/number@3.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 - '@inquirer/password@4.0.2(@types/node@22.9.0)': + '@inquirer/password@4.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 ansi-escapes: 4.3.2 - '@inquirer/prompts@7.1.0(@types/node@22.9.0)': - dependencies: - '@inquirer/checkbox': 4.0.2(@types/node@22.9.0) - '@inquirer/confirm': 5.0.2(@types/node@22.9.0) - '@inquirer/editor': 4.1.0(@types/node@22.9.0) - '@inquirer/expand': 4.0.2(@types/node@22.9.0) - '@inquirer/input': 4.0.2(@types/node@22.9.0) - '@inquirer/number': 3.0.2(@types/node@22.9.0) - '@inquirer/password': 4.0.2(@types/node@22.9.0) - '@inquirer/rawlist': 4.0.2(@types/node@22.9.0) - '@inquirer/search': 3.0.2(@types/node@22.9.0) - '@inquirer/select': 4.0.2(@types/node@22.9.0) - '@types/node': 22.9.0 - - '@inquirer/rawlist@4.0.2(@types/node@22.9.0)': - dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/prompts@7.1.0(@types/node@22.10.0)': + dependencies: + '@inquirer/checkbox': 4.0.2(@types/node@22.10.0) + '@inquirer/confirm': 5.0.2(@types/node@22.10.0) + '@inquirer/editor': 4.1.0(@types/node@22.10.0) + '@inquirer/expand': 4.0.2(@types/node@22.10.0) + '@inquirer/input': 4.0.2(@types/node@22.10.0) + '@inquirer/number': 3.0.2(@types/node@22.10.0) + '@inquirer/password': 4.0.2(@types/node@22.10.0) + '@inquirer/rawlist': 4.0.2(@types/node@22.10.0) + '@inquirer/search': 3.0.2(@types/node@22.10.0) + '@inquirer/select': 4.0.2(@types/node@22.10.0) + '@types/node': 22.10.0 + + '@inquirer/rawlist@4.0.2(@types/node@22.10.0)': + dependencies: + '@inquirer/core': 10.1.0(@types/node@22.10.0) + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 yoctocolors-cjs: 2.1.2 - '@inquirer/search@3.0.2(@types/node@22.9.0)': + '@inquirer/search@3.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) + '@inquirer/core': 10.1.0(@types/node@22.10.0) '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 yoctocolors-cjs: 2.1.2 - '@inquirer/select@4.0.2(@types/node@22.9.0)': + '@inquirer/select@4.0.2(@types/node@22.10.0)': dependencies: - '@inquirer/core': 10.1.0(@types/node@22.9.0) + '@inquirer/core': 10.1.0(@types/node@22.10.0) '@inquirer/figures': 1.0.8 - '@inquirer/type': 3.0.1(@types/node@22.9.0) - '@types/node': 22.9.0 + '@inquirer/type': 3.0.1(@types/node@22.10.0) + '@types/node': 22.10.0 ansi-escapes: 4.3.2 yoctocolors-cjs: 2.1.2 @@ -7512,9 +7645,9 @@ snapshots: dependencies: mute-stream: 1.0.0 - '@inquirer/type@3.0.1(@types/node@22.9.0)': + '@inquirer/type@3.0.1(@types/node@22.10.0)': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@isaacs/cliui@8.0.2': dependencies: @@ -7571,9 +7704,9 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.1.0(@types/node@22.9.0))': + '@listr2/prompt-adapter-inquirer@2.0.18(@inquirer/prompts@7.1.0(@types/node@22.10.0))': dependencies: - '@inquirer/prompts': 7.1.0(@types/node@22.9.0) + '@inquirer/prompts': 7.1.0(@types/node@22.10.0) '@inquirer/type': 1.5.5 '@lmdb/lmdb-darwin-arm64@3.1.5': @@ -7680,9 +7813,9 @@ snapshots: '@napi-rs/nice-win32-x64-msvc': 1.0.1 optional: true - '@ngtools/webpack@19.0.0-rc.3(@angular/compiler-cli@19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(typescript@5.6.3)(webpack@5.96.1(esbuild@0.24.0))': + '@ngtools/webpack@19.0.2(@angular/compiler-cli@19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3))(typescript@5.6.3)(webpack@5.96.1(esbuild@0.24.0))': dependencies: - '@angular/compiler-cli': 19.0.0-rc.3(@angular/compiler@19.0.0-rc.3(@angular/core@19.0.0-rc.3(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) + '@angular/compiler-cli': 19.0.1(@angular/compiler@19.0.1(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)))(typescript@5.6.3) typescript: 5.6.3 webpack: 5.96.1(esbuild@0.24.0) @@ -7753,7 +7886,7 @@ snapshots: dependencies: '@npmcli/git': 6.0.1 glob: 10.4.5 - hosted-git-info: 8.0.0 + hosted-git-info: 8.0.2 json-parse-even-better-errors: 4.0.0 normalize-package-data: 7.0.0 proc-log: 5.0.0 @@ -7899,10 +8032,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.26.0': optional: true - '@schematics/angular@19.0.0-rc.3(chokidar@4.0.1)': + '@schematics/angular@19.0.2(chokidar@4.0.1)': dependencies: - '@angular-devkit/core': 19.0.0-rc.3(chokidar@4.0.1) - '@angular-devkit/schematics': 19.0.0-rc.3(chokidar@4.0.1) + '@angular-devkit/core': 19.0.2(chokidar@4.0.1) + '@angular-devkit/schematics': 19.0.2(chokidar@4.0.1) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -7945,7 +8078,11 @@ snapshots: '@thednp/event-listener@2.0.8': {} - '@thednp/shorty@2.0.7': {} + '@thednp/position-observer@1.0.5': + dependencies: + '@thednp/shorty': 2.0.9 + + '@thednp/shorty@2.0.9': {} '@ts-morph/common@0.25.0': dependencies: @@ -7963,26 +8100,26 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/connect-history-api-fallback@1.5.4': dependencies: - '@types/express-serve-static-core': 5.0.1 - '@types/node': 22.9.0 + '@types/express-serve-static-core': 5.0.2 + '@types/node': 22.10.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/cookie@0.4.1': {} '@types/cors@2.8.17': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/debug@4.1.12': dependencies: @@ -8002,14 +8139,14 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/qs': 6.9.17 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 - '@types/express-serve-static-core@5.0.1': + '@types/express-serve-static-core@5.0.2': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/qs': 6.9.17 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -8025,7 +8162,7 @@ snapshots: '@types/http-proxy@1.17.15': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/jasmine@5.1.4': {} @@ -8041,11 +8178,11 @@ snapshots: '@types/node-forge@1.3.11': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 - '@types/node@22.9.0': + '@types/node@22.10.0': dependencies: - undici-types: 6.19.8 + undici-types: 6.20.0 '@types/qs@6.9.17': {} @@ -8056,7 +8193,7 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/serve-index@1.9.4': dependencies: @@ -8065,43 +8202,43 @@ snapshots: '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/send': 0.17.4 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 '@types/unist@3.0.3': {} '@types/ws@8.5.13': dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 - '@typescript-eslint/eslint-plugin@8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/scope-manager': 8.15.0 - '@typescript-eslint/type-utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.15.0 + '@typescript-eslint/parser': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.16.0 + '@typescript-eslint/type-utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.16.0 eslint: 9.15.0(jiti@1.21.6) graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.2(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@typescript-eslint/scope-manager': 8.15.0 - '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) - '@typescript-eslint/visitor-keys': 8.15.0 + '@typescript-eslint/scope-manager': 8.16.0 + '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) + '@typescript-eslint/visitor-keys': 8.16.0 debug: 4.3.7 eslint: 9.15.0(jiti@1.21.6) optionalDependencies: @@ -8109,60 +8246,60 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.15.0': + '@typescript-eslint/scope-manager@8.16.0': dependencies: - '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/visitor-keys': 8.15.0 + '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/visitor-keys': 8.16.0 - '@typescript-eslint/type-utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@typescript-eslint/type-utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) debug: 4.3.7 eslint: 9.15.0(jiti@1.21.6) - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.2(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.15.0': {} + '@typescript-eslint/types@8.16.0': {} - '@typescript-eslint/typescript-estree@8.15.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@8.16.0(typescript@5.6.3)': dependencies: - '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/visitor-keys': 8.15.0 + '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/visitor-keys': 8.16.0 debug: 4.3.7 fast-glob: 3.3.2 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.4.0(typescript@5.6.3) + ts-api-utils: 1.4.2(typescript@5.6.3) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': + '@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3)': dependencies: '@eslint-community/eslint-utils': 4.4.1(eslint@9.15.0(jiti@1.21.6)) - '@typescript-eslint/scope-manager': 8.15.0 - '@typescript-eslint/types': 8.15.0 - '@typescript-eslint/typescript-estree': 8.15.0(typescript@5.6.3) + '@typescript-eslint/scope-manager': 8.16.0 + '@typescript-eslint/types': 8.16.0 + '@typescript-eslint/typescript-estree': 8.16.0(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) optionalDependencies: typescript: 5.6.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.15.0': + '@typescript-eslint/visitor-keys@8.16.0': dependencies: - '@typescript-eslint/types': 8.15.0 + '@typescript-eslint/types': 8.16.0 eslint-visitor-keys: 4.2.0 - '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': + '@vitejs/plugin-basic-ssl@1.1.0(vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0))': dependencies: - vite: 5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) + vite: 5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -8306,22 +8443,28 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - angular-eslint@18.4.1(@angular-devkit/core@19.0.0-rc.3(chokidar@4.0.1))(@angular-devkit/schematics@19.0.0-rc.3(chokidar@4.0.1))(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript-eslint@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(typescript@5.6.3): + angular-eslint@19.0.0-alpha.4(@angular-devkit/core@19.0.2(chokidar@4.0.1))(@angular-devkit/schematics@19.0.2(chokidar@4.0.1))(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript-eslint@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(typescript@5.6.3): dependencies: - '@angular-eslint/builder': 18.4.1(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@angular-eslint/eslint-plugin': 18.4.1(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@angular-eslint/eslint-plugin-template': 18.4.1(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@angular-eslint/schematics': 18.4.1(@angular-devkit/core@19.0.0-rc.3(chokidar@4.0.1))(@angular-devkit/schematics@19.0.0-rc.3(chokidar@4.0.1))(@typescript-eslint/types@8.15.0)(@typescript-eslint/utils@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@angular-eslint/template-parser': 18.4.1(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/builder': 19.0.0-alpha.4(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/eslint-plugin': 19.0.0-alpha.4(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/eslint-plugin-template': 19.0.0-alpha.4(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/schematics': 19.0.0-alpha.4(@angular-devkit/core@19.0.2(chokidar@4.0.1))(@angular-devkit/schematics@19.0.2(chokidar@4.0.1))(@typescript-eslint/types@8.16.0)(@typescript-eslint/utils@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@angular-eslint/template-parser': 19.0.0-alpha.4(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) typescript: 5.6.3 - typescript-eslint: 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + typescript-eslint: 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) transitivePeerDependencies: - '@angular-devkit/core' - '@angular-devkit/schematics' - '@typescript-eslint/types' - '@typescript-eslint/utils' + angularx-qrcode@19.0.0(@angular/core@19.0.1(rxjs@7.8.1)(zone.js@0.15.0)): + dependencies: + '@angular/core': 19.0.1(rxjs@7.8.1)(zone.js@0.15.0) + qrcode: 1.5.4 + tslib: 2.8.1 + ansi-colors@4.1.3: {} ansi-escapes@4.3.2: @@ -8370,7 +8513,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.4.49): dependencies: browserslist: 4.24.2 - caniuse-lite: 1.0.30001680 + caniuse-lite: 1.0.30001684 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -8493,10 +8636,11 @@ snapshots: boolbase@1.0.0: {} - bootstrap.native@5.0.13: + bootstrap.native@5.1.1: dependencies: '@thednp/event-listener': 2.0.8 - '@thednp/shorty': 2.0.7 + '@thednp/position-observer': 1.0.5 + '@thednp/shorty': 2.0.9 brace-expansion@1.1.11: dependencies: @@ -8513,8 +8657,8 @@ snapshots: browserslist@4.24.2: dependencies: - caniuse-lite: 1.0.30001680 - electron-to-chromium: 1.5.63 + caniuse-lite: 1.0.30001684 + electron-to-chromium: 1.5.65 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) @@ -8575,7 +8719,9 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001680: {} + camelcase@5.3.1: {} + + caniuse-lite@1.0.30001684: {} ccount@2.0.1: {} @@ -8648,6 +8794,12 @@ snapshots: cli-width@4.1.0: {} + cliui@6.0.0: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + cliui@7.0.4: dependencies: string-width: 4.2.3 @@ -8822,6 +8974,8 @@ snapshots: dependencies: callsite: 1.0.0 + decamelize@1.2.0: {} + decode-named-character-reference@1.0.2: dependencies: character-entities: 2.0.2 @@ -8873,6 +9027,8 @@ snapshots: didyoumean@1.2.2: {} + dijkstrajs@1.0.3: {} + dlv@1.1.3: {} dns-packet@5.6.1: @@ -8912,7 +9068,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.63: {} + electron-to-chromium@1.5.65: {} emitter-component@1.1.2: {} @@ -8943,7 +9099,7 @@ snapshots: dependencies: '@types/cookie': 0.4.1 '@types/cors': 2.8.17 - '@types/node': 22.9.0 + '@types/node': 22.10.0 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 @@ -9067,16 +9223,16 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)): + eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.3.7 enhanced-resolve: 5.17.1 eslint: 9.15.0(jiti@1.21.6) - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) fast-glob: 3.3.2 get-tsconfig: 4.8.1 - is-bun-module: 1.2.1 + is-bun-module: 1.3.0 is-glob: 4.0.3 transitivePeerDependencies: - '@typescript-eslint/parser' @@ -9084,23 +9240,23 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.12.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)): + eslint-module-utils@2.12.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/parser': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)) + eslint-import-resolver-typescript: 3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)) transitivePeerDependencies: - supports-color - eslint-plugin-boundaries@5.0.1(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)): + eslint-plugin-boundaries@5.0.1(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)): dependencies: chalk: 4.1.2 eslint: 9.15.0(jiti@1.21.6) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) + eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.3(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6)))(eslint@9.15.0(jiti@1.21.6)) micromatch: 4.0.8 transitivePeerDependencies: - '@typescript-eslint/parser' @@ -9108,7 +9264,7 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsdoc@50.5.0(eslint@9.15.0(jiti@1.21.6)): + eslint-plugin-jsdoc@50.6.0(eslint@9.15.0(jiti@1.21.6)): dependencies: '@es-joy/jsdoccomment': 0.49.0 are-docs-informative: 0.0.2 @@ -9357,6 +9513,11 @@ snapshots: common-path-prefix: 3.0.0 pkg-dir: 7.0.0 + find-up@4.1.0: + dependencies: + locate-path: 5.0.0 + path-exists: 4.0.0 + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -9527,7 +9688,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hosted-git-info@8.0.0: + hosted-git-info@8.0.2: dependencies: lru-cache: 10.4.3 @@ -9637,7 +9798,7 @@ snapshots: hyperdyperid@1.2.0: {} - i18next@23.16.5: + i18next@23.16.8: dependencies: '@babel/runtime': 7.26.0 @@ -9666,7 +9827,7 @@ snapshots: image-size@0.5.5: optional: true - immutable@5.0.2: {} + immutable@5.0.3: {} import-fresh@3.3.0: dependencies: @@ -9703,7 +9864,7 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-bun-module@1.2.1: + is-bun-module@1.3.0: dependencies: semver: 7.6.3 @@ -9830,7 +9991,7 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -9876,6 +10037,8 @@ snapshots: jsonparse@1.3.1: {} + jwt-decode@4.0.0: {} + karma-chrome-launcher@3.2.0: dependencies: which: 1.3.1 @@ -10023,6 +10186,10 @@ snapshots: loader-utils@3.3.1: {} + locate-path@5.0.0: + dependencies: + p-locate: 4.1.0 + locate-path@6.0.0: dependencies: p-locate: 5.0.0 @@ -10269,7 +10436,7 @@ snapshots: micromark-util-html-tag-name: 2.0.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 - micromark-util-subtokenize: 2.0.2 + micromark-util-subtokenize: 2.0.3 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 @@ -10412,7 +10579,7 @@ snapshots: micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 - micromark-util-subtokenize@2.0.2: + micromark-util-subtokenize@2.0.3: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.1 @@ -10439,7 +10606,7 @@ snapshots: micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 micromark-util-sanitize-uri: 2.0.1 - micromark-util-subtokenize: 2.0.2 + micromark-util-subtokenize: 2.0.3 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.1 transitivePeerDependencies: @@ -10596,7 +10763,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.7: {} + nanoid@3.3.8: {} natural-compare@1.4.0: {} @@ -10652,7 +10819,7 @@ snapshots: normalize-package-data@7.0.0: dependencies: - hosted-git-info: 8.0.0 + hosted-git-info: 8.0.2 semver: 7.6.3 validate-npm-package-license: 3.0.4 @@ -10664,7 +10831,7 @@ snapshots: dependencies: npm-normalize-package-bin: 4.0.0 - npm-install-checks@7.1.0: + npm-install-checks@7.1.1: dependencies: semver: 7.6.3 @@ -10672,7 +10839,7 @@ snapshots: npm-package-arg@12.0.0: dependencies: - hosted-git-info: 8.0.0 + hosted-git-info: 8.0.2 proc-log: 5.0.0 semver: 7.6.3 validate-npm-package-name: 6.0.0 @@ -10683,7 +10850,7 @@ snapshots: npm-pick-manifest@10.0.0: dependencies: - npm-install-checks: 7.1.0 + npm-install-checks: 7.1.1 npm-normalize-package-bin: 4.0.0 npm-package-arg: 12.0.0 semver: 7.6.3 @@ -10717,6 +10884,10 @@ snapshots: obuf@1.1.2: {} + oidc-client-ts@3.1.0: + dependencies: + jwt-decode: 4.0.0 + on-finished@2.3.0: dependencies: ee-first: 1.1.1 @@ -10785,6 +10956,10 @@ snapshots: os-tmpdir@1.0.2: {} + p-limit@2.3.0: + dependencies: + p-try: 2.2.0 + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 @@ -10793,6 +10968,10 @@ snapshots: dependencies: yocto-queue: 1.1.1 + p-locate@4.1.0: + dependencies: + p-limit: 2.3.0 + p-locate@5.0.0: dependencies: p-limit: 3.1.0 @@ -10813,6 +10992,8 @@ snapshots: is-network-error: 1.1.0 retry: 0.13.1 + p-try@2.2.0: {} + package-json-from-dist@1.0.1: {} pacote@20.0.0: @@ -10928,6 +11109,8 @@ snapshots: dependencies: find-up: 6.3.0 + pngjs@5.0.0: {} + postcss-import@15.1.0(postcss@8.4.49): dependencies: postcss: 8.4.49 @@ -10943,7 +11126,7 @@ snapshots: postcss-load-config@4.0.2(postcss@8.4.49): dependencies: lilconfig: 3.1.2 - yaml: 2.6.0 + yaml: 2.6.1 optionalDependencies: postcss: 8.4.49 @@ -11000,7 +11183,7 @@ snapshots: postcss@8.4.49: dependencies: - nanoid: 3.3.7 + nanoid: 3.3.8 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -11010,7 +11193,7 @@ snapshots: dependencies: fast-diff: 1.3.0 - prettier-plugin-tailwindcss@0.6.8(prettier@3.3.3): + prettier-plugin-tailwindcss@0.6.9(prettier@3.3.3): dependencies: prettier: 3.3.3 @@ -11056,6 +11239,12 @@ snapshots: qjobs@1.2.0: {} + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + qs@6.13.0: dependencies: side-channel: 1.0.6 @@ -11117,18 +11306,18 @@ snapshots: regex-parser@2.3.0: {} - regexpu-core@6.1.1: + regexpu-core@6.2.0: dependencies: regenerate: 1.4.2 regenerate-unicode-properties: 10.2.0 regjsgen: 0.8.0 - regjsparser: 0.11.2 + regjsparser: 0.12.0 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.2.0 regjsgen@0.8.0: {} - regjsparser@0.11.2: + regjsparser@0.12.0: dependencies: jsesc: 3.0.2 @@ -11136,6 +11325,8 @@ snapshots: require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} + requires-port@1.0.0: {} resolve-from@4.0.0: {} @@ -11232,7 +11423,7 @@ snapshots: sass@1.80.7: dependencies: chokidar: 4.0.1 - immutable: 5.0.2 + immutable: 5.0.3 source-map-js: 1.2.1 optionalDependencies: '@parcel/watcher': 2.5.0 @@ -11327,6 +11518,8 @@ snapshots: transitivePeerDependencies: - supports-color + set-blocking@2.0.0: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -11700,7 +11893,7 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.4.0(typescript@5.6.3): + ts-api-utils@1.4.2(typescript@5.6.3): dependencies: typescript: 5.6.3 @@ -11734,11 +11927,11 @@ snapshots: typed-assert@1.0.9: {} - typescript-eslint@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3): + typescript-eslint@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.15.0(@typescript-eslint/parser@8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/parser': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) - '@typescript-eslint/utils': 8.15.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/eslint-plugin': 8.16.0(@typescript-eslint/parser@8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/parser': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) + '@typescript-eslint/utils': 8.16.0(eslint@9.15.0(jiti@1.21.6))(typescript@5.6.3) eslint: 9.15.0(jiti@1.21.6) optionalDependencies: typescript: 5.6.3 @@ -11752,7 +11945,7 @@ snapshots: uglify-js@3.19.3: optional: true - undici-types@6.19.8: {} + undici-types@6.20.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -11845,13 +12038,13 @@ snapshots: moment: 2.30.1 propagating-hammerjs: 1.5.0 - vite@5.4.11(@types/node@22.9.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0): + vite@5.4.11(@types/node@22.10.0)(less@4.2.0)(sass@1.80.7)(terser@5.36.0): dependencies: esbuild: 0.21.5 postcss: 8.4.49 rollup: 4.26.0 optionalDependencies: - '@types/node': 22.9.0 + '@types/node': 22.10.0 fsevents: 2.3.3 less: 4.2.0 sass: 1.80.7 @@ -11975,6 +12168,8 @@ snapshots: websocket-extensions@0.1.4: {} + which-module@2.0.1: {} + which@1.3.1: dependencies: isexe: 2.0.0 @@ -12031,6 +12226,8 @@ snapshots: ws@8.18.0: {} + y18n@4.0.3: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -12039,12 +12236,31 @@ snapshots: yallist@5.0.0: {} - yaml@2.6.0: {} + yaml@2.6.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 yargs-parser@20.2.9: {} yargs-parser@21.1.1: {} + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + yargs@16.2.0: dependencies: cliui: 7.0.4 diff --git a/src/app/app.component.html b/src/app/app.component.html index 9c67d58..0e8137f 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -4,6 +4,7 @@
+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index e23ece9..70ff7c2 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -1,5 +1,6 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; +import { ShowSessionComponent } from '@app/portal/features/show-session/show-session.component'; import { SiteFooterComponent } from '@app/portal/features/site-footer/site-footer.component'; import { SiteHeaderComponent } from '@app/portal/features/site-header/site-header.component'; @@ -8,7 +9,12 @@ import { SiteHeaderComponent } from '@app/portal/features/site-header/site-heade */ @Component({ selector: 'app-root', - imports: [RouterOutlet, SiteHeaderComponent, SiteFooterComponent], + imports: [ + RouterOutlet, + SiteHeaderComponent, + SiteFooterComponent, + ShowSessionComponent, + ], templateUrl: './app.component.html', }) export class AppComponent {} diff --git a/src/app/app.config.ts b/src/app/app.config.ts index d13efa4..cc73d7e 100644 --- a/src/app/app.config.ts +++ b/src/app/app.config.ts @@ -2,10 +2,24 @@ import { ApplicationConfig, provideExperimentalZonelessChangeDetection, } from '@angular/core'; +import { provideNoopAnimations } from '@angular/platform-browser/animations'; import { provideRouter } from '@angular/router'; +import { + HTTP_INTERCEPTORS, + provideHttpClient, + withFetch, + withInterceptorsFromDi, +} from '@angular/common/http'; +import { CsrfInterceptor } from '@app/auth/services/csrf.service'; import { routes } from './app.routes'; export const appConfig: ApplicationConfig = { - providers: [provideExperimentalZonelessChangeDetection(), provideRouter(routes)], + providers: [ + provideExperimentalZonelessChangeDetection(), + provideRouter(routes), + provideHttpClient(withFetch(), withInterceptorsFromDi()), + provideNoopAnimations(), + { provide: HTTP_INTERCEPTORS, useClass: CsrfInterceptor, multi: true }, + ], }; diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index e300ac5..601740b 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -1,5 +1,8 @@ /* eslint-disable jsdoc/require-jsdoc */ +import { inject } from '@angular/core'; import { Routes } from '@angular/router'; +import { AuthService } from '@app/auth/services/auth.service'; + export const routes: Routes = [ { path: '', @@ -15,4 +18,33 @@ export const routes: Routes = [ (m) => m.MetadataBrowserComponent, ), }, + // routes used in the authentication flows + { + path: 'oauth/callback', + canActivate: [() => inject(AuthService).oidcRedirect()], + children: [], + }, + // TODO: add guards to the following routes that check the expected state + // TODO: also add deactivation guards to these routes + { + path: 'register', + loadComponent: () => + import('./auth/features/register/register.component').then( + (m) => m.RegisterComponent, + ), + }, + { + path: 'setup-totp', + loadComponent: () => + import('./auth/features/setup-totp/setup-totp.component').then( + (m) => m.SetupTotpComponent, + ), + }, + { + path: 'confirm-totp', + loadComponent: () => + import('./auth/features/confirm-totp/confirm-totp.component').then( + (m) => m.ConfirmTotpComponent, + ), + }, ]; diff --git a/src/app/auth/features/confirm-totp/confirm-totp.component.html b/src/app/auth/features/confirm-totp/confirm-totp.component.html new file mode 100644 index 0000000..0601f46 --- /dev/null +++ b/src/app/auth/features/confirm-totp/confirm-totp.component.html @@ -0,0 +1,22 @@ +

Two-factor authentication

+ +

Please enter the 6-digit authentication code generated by your authenticator app

+ + + Authentication Code + + + + + +@if (verificationError) { + The entered authentication code was not valid. +} + + diff --git a/src/app/auth/features/confirm-totp/confirm-totp.component.spec.ts b/src/app/auth/features/confirm-totp/confirm-totp.component.spec.ts new file mode 100644 index 0000000..b12e963 --- /dev/null +++ b/src/app/auth/features/confirm-totp/confirm-totp.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ConfirmTotpComponent } from './confirm-totp.component'; + +describe('ConfirmTotpComponent', () => { + let component: ConfirmTotpComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ConfirmTotpComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ConfirmTotpComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/features/confirm-totp/confirm-totp.component.ts b/src/app/auth/features/confirm-totp/confirm-totp.component.ts new file mode 100644 index 0000000..e269e98 --- /dev/null +++ b/src/app/auth/features/confirm-totp/confirm-totp.component.ts @@ -0,0 +1,72 @@ +import { Component, inject } from '@angular/core'; +import { + FormControl, + FormsModule, + ReactiveFormsModule, + Validators, +} from '@angular/forms'; +import { MatButtonModule } from '@angular/material/button'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatInputModule } from '@angular/material/input'; +import { AuthService } from '@app/auth/services/auth.service'; + +// TODO: Polish this component + +/** + * TOTP confirmation page + */ +@Component({ + selector: 'app-confirm-totp', + imports: [ + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatFormFieldModule, + MatInputModule, + ], + templateUrl: './confirm-totp.component.html', +}) +export class ConfirmTotpComponent { + #authService = inject(AuthService); + + codeControl = new FormControl('', [ + Validators.required, + Validators.pattern(/^\d{6}$/), + ]); + + verificationError = false; + + /** + * Input handler for the TOTP code + */ + onInput(): void { + this.verificationError = false; + } + + /** + * Submit authentication code + */ + async onSubmit(): Promise { + this.verificationError = false; + const code = this.codeControl.value; + if (!code) return; + const verified = await this.#authService.verifyTotpCode(code); + if (verified) { + console.info('Successfully authenticated.'); + // add toast message here + this.#authService.redirectAfterLogin(); + } else { + // maybe add toast message here + console.error('Failed to authenticate.'); + this.codeControl.reset(); + this.verificationError = true; + } + } + + /** + * Set status to "lost token" and redirect to token setup + */ + onLostToken(): void { + this.#authService.lostTotpSetup(); + } +} diff --git a/src/app/auth/features/register/register.component.html b/src/app/auth/features/register/register.component.html new file mode 100644 index 0000000..0c30abe --- /dev/null +++ b/src/app/auth/features/register/register.component.html @@ -0,0 +1,66 @@ +

Registration with GHGA

+@let u = user(); +@switch (u) { + @case (undefined) { +

Loading...

+ } + @case (null) { +

You are currently not logged in.

+

Go back to the home page.

+ } + @default { +

Welcome, {{ u.full_name }}!

+

+ @if (u.id) { + @if (u.state === 'NeedsReRegistration') { + Your contact information has changed since you last registered. + } + Please confirm that the information given below is correct. + } @else { + Since you haven't used our data portal before, we ask you to confirm your user + data and register with us. + } +

+

+ Name: + {{ u.name }} +

+

+ E-Mail: + {{ u.email }} +

+

+ Life Science ID: + {{ u.ext_id }} +

+

+ Academic title: + + + @for (title of allTitles; track title) { + {{ title || '–' }} + } + + +

+

+ + I accept the + terms of use + and the + + privacy policy. + +

+ + + } +} diff --git a/src/app/auth/features/register/register.component.scss b/src/app/auth/features/register/register.component.scss new file mode 100644 index 0000000..c42a18c --- /dev/null +++ b/src/app/auth/features/register/register.component.scss @@ -0,0 +1,5 @@ +.label { + display: inline-block; + font-weight: bold; + min-width: 9em; +} diff --git a/src/app/auth/features/register/register.component.spec.ts b/src/app/auth/features/register/register.component.spec.ts new file mode 100644 index 0000000..54d1433 --- /dev/null +++ b/src/app/auth/features/register/register.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let component: RegisterComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RegisterComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(RegisterComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/features/register/register.component.ts b/src/app/auth/features/register/register.component.ts new file mode 100644 index 0000000..fc1bb66 --- /dev/null +++ b/src/app/auth/features/register/register.component.ts @@ -0,0 +1,81 @@ +import { Component, effect, inject } from '@angular/core'; +import { FormControl, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatFormFieldModule } from '@angular/material/form-field'; +import { MatSelectModule } from '@angular/material/select'; + +import { AcademicTitle, UserBasicData } from '@app/auth/models/user'; + +import { MatButtonModule } from '@angular/material/button'; +import { AuthService } from '@app/auth/services/auth.service'; + +// TODO: Polish this component + +/** + * User registration page + */ +@Component({ + selector: 'app-register', + imports: [ + FormsModule, + ReactiveFormsModule, + MatButtonModule, + MatCheckboxModule, + MatFormFieldModule, + MatSelectModule, + ], + templateUrl: './register.component.html', + styleUrl: './register.component.scss', +}) +export class RegisterComponent { + #authService = inject(AuthService); + + user = this.#authService.user; + + allTitles: AcademicTitle[] = [null, 'Dr.', 'Prof.']; + + titleControl = new FormControl(null); + + accepted = false; + + /** + * Initialize the registration component + */ + constructor() { + // if a re-registering user already has a title, + // then set it as value in the control + effect(() => { + const title = this.user()?.title; + if (title) { + this.titleControl.setValue(title); + } + }); + } + + /** + * Cancel registration and log out + */ + async cancel(): Promise { + await this.#authService.logout(); + } + + /** + * Submit registration form + */ + async register(): Promise { + if (!this.accepted) return; + const user = this.user(); + if (!user) return; + const title = this.titleControl.value || null; + const { id, ext_id, name, email } = user; + const data: UserBasicData = { name, email, title }; + const ok = await this.#authService.register(id || null, ext_id, data); + if (ok) { + // add toast message or dialog here + console.info('Registration was successful.'); + } else { + // add toast message here + console.error('Registration failed.'); + } + } +} diff --git a/src/app/auth/features/setup-totp/setup-totp.component.html b/src/app/auth/features/setup-totp/setup-totp.component.html new file mode 100644 index 0000000..27cc1a9 --- /dev/null +++ b/src/app/auth/features/setup-totp/setup-totp.component.html @@ -0,0 +1,61 @@ +

Set up two-factor authentication

+ +@if (isLoading()) { +

Loading...

+} @else if (isNewlyRegistered()) { +

Thank you for registering in the GHGA data portal.

+

+ For additional security when accessing protected data with the GHGA data portal, we + are using two-factor authentication and verification of your identity via a contact + address that is separate from your primary E-Mail contact address. +

+ +} @else if (hasLostToken() && !lostTokenConfirmed()) { +

+ In the case that you lost your phone or the setup of your authenticator app, you can + create a new authentication code setup. +

+

+ However, all contact addresses that had been verified before will need to be + verified again. +

+ + +} @else { + @let uri = setupUri() | async; + @if (uri) { +

+ The two-factor authentication means that we require you to enter an additional + 6-digit authentication code after you logged in via LS Login. This code can be + produced by an authenticator app such as Aegis, Microsoft Authenticator or Google + Authenticator, which you can install on your mobile phone. +

+

+ In order to set up the authenticator app to produce the authentication codes for + the GHGA data portal, please scan this QR code with your authenticator app: +

+ + + @if (showManualSetup) { +

+ If you have trouble scanning the QR Code, please manually input the following + setup key in your authenticator app: +

+

+ + } + + } @else if (uri === undefined) { +

Generating the TOTP setup...

+ } @else { +

Error setting up two-factor authentication.

+

Go back to the home page.

+ } +} diff --git a/src/app/auth/features/setup-totp/setup-totp.component.spec.ts b/src/app/auth/features/setup-totp/setup-totp.component.spec.ts new file mode 100644 index 0000000..80d09b2 --- /dev/null +++ b/src/app/auth/features/setup-totp/setup-totp.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SetupTotpComponent } from './setup-totp.component'; + +describe('SetupTotpComponent', () => { + let component: SetupTotpComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SetupTotpComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(SetupTotpComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/auth/features/setup-totp/setup-totp.component.ts b/src/app/auth/features/setup-totp/setup-totp.component.ts new file mode 100644 index 0000000..8eeec27 --- /dev/null +++ b/src/app/auth/features/setup-totp/setup-totp.component.ts @@ -0,0 +1,82 @@ +import { CommonModule } from '@angular/common'; +import { Component, computed, inject, signal } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { AuthService } from '@app/auth/services/auth.service'; +import { QRCodeComponent } from 'angularx-qrcode'; + +// TODO: Polish this component +// TODO: Maybe show another warning or different text when state is "LostTotpToken" + +/** + * TOTP setup page + */ +@Component({ + selector: 'app-setup-totp', + imports: [CommonModule, QRCodeComponent, MatButtonModule], + templateUrl: './setup-totp.component.html', +}) +export class SetupTotpComponent { + #authService = inject(AuthService); + #sessionState = this.#authService.sessionState; + + isLoading = this.#authService.isUndetermined; + + isNewlyRegistered = computed(() => this.#sessionState() === 'Registered'); + + hasLostToken = computed(() => this.#sessionState() === 'LostTotpToken'); + + lostTokenConfirmed = signal(false); + + /** + * Get the provisioning URI as a signal + */ + setupUri = computed(async () => { + const state = this.#sessionState(); + if (!['NeedsTotpToken', 'LostTotpToken'].includes(state)) return null; + const uri = await this.#authService.createTotpToken(); + return uri; + }); + + showManualSetup = false; + + /** + * Confirm the registration status and proceed with the TOTP setup + */ + confirmRegistration(): void { + this.#authService.needsTotpSetup(); + } + + /** + * Cancel the recreation of a lost TOTP token + */ + cancelLostToken(): void { + this.#authService.completeTotpSetup(); + } + + /** + * Get the actual secret from the query parameter in the URI + * @param uri The provisioning URI containing the secret + * @returns just the secret from the URI + */ + getSecret(uri: string): string { + return new URLSearchParams(uri.substring(uri.indexOf('?'))).get('secret')!; + } + + /** + * Copy the secret to the clipboard + * @param uri The provisioning URI containing the secret + */ + copySecret(uri: string): void { + navigator.clipboard.writeText(this.getSecret(uri)); + } + + /** + * Complete the TOTP setup + * + * Assume that the user has added the TOTP token to the authenticator + * and continue with the TOTP confirmation step. + */ + completeSetup(): void { + this.#authService.completeTotpSetup(); + } +} diff --git a/src/app/auth/models/user.ts b/src/app/auth/models/user.ts new file mode 100644 index 0000000..7e9b87a --- /dev/null +++ b/src/app/auth/models/user.ts @@ -0,0 +1,54 @@ +/** + * All possible states of the user session + */ +export type LoginState = + | 'Undetermined' // the state is not yet determined + | 'LoggedOut' // the user is logged out + | 'LoggedIn' // the user is logged in with first factor + | 'NeedsRegistration' // the user needs to register (backend state) + | 'NeedsReRegistration' // the user needs to re-register (backend state) + | 'Registered' // the user is registered (backend state) + | 'NeedsTotpToken' // the user needs to set up TOTP (frontend state only) + | 'LostTotpToken' // the user has lost the TOTP token (frontend state only) + | 'NewTotpToken' // a new TOTP token has just been created (backend state) + | 'HasTotpToken' // a TOTP token exists but not yet authenticated (backend state) + | 'Authenticated'; // user is fully authenticated with second factor (backend state) + +/** + * All possible academic titles + */ +export type AcademicTitle = null | 'Dr.' | 'Prof.'; + +/** + * Basic data of a user + */ +export interface UserBasicData { + name: string; + title?: AcademicTitle; + email: string; +} + +/** + * Basic data of a registered user + */ +export interface UserRegisteredData extends UserBasicData { + ext_id: string; +} + +/** + * User session interface + * + * Contains all data describing the user and the user session. + * + * Note that this is different from the low-level oidcUser object, + * which does not contain the user data from the backend. + */ +export interface User extends UserRegisteredData { + id?: string; + full_name: string; + state: LoginState; + role?: string; + csrf: string; + timeout?: number; + extends?: number; +} diff --git a/src/app/auth/services/auth.service.spec.ts b/src/app/auth/services/auth.service.spec.ts new file mode 100644 index 0000000..f1251ca --- /dev/null +++ b/src/app/auth/services/auth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/auth/services/auth.service.ts b/src/app/auth/services/auth.service.ts new file mode 100644 index 0000000..c346e64 --- /dev/null +++ b/src/app/auth/services/auth.service.ts @@ -0,0 +1,459 @@ +import { + HttpClient, + HttpHeaders, + HttpParams, + HttpResponse, +} from '@angular/common/http'; +import { computed, inject, Injectable, signal } from '@angular/core'; +import { Router } from '@angular/router'; +import { ConfigService } from '@app/shared/services/config.service'; +import type { OidcMetadata, UserManagerSettings } from 'oidc-client-ts'; +import { + Log as OidcLog, + User as OidcUser, + UserManager as OidcUserManager, +} from 'oidc-client-ts'; +import { catchError, firstValueFrom, map, Observable, of } from 'rxjs'; +import { LoginState, User, UserBasicData } from '../models/user'; +import { CsrfService } from './csrf.service'; + +/** + * Authentication service + * + * This service provides the OIDC and 2FA related functionality + * and keeps track of the state of the user session. + */ +@Injectable({ providedIn: 'root' }) +export class AuthService { + #config = inject(ConfigService); + #http = inject(HttpClient); + #router = inject(Router); + #csrf = inject(CsrfService); + #userSignal = signal(undefined); + + #redirectAfterLogin = '/'; + + #oidcUserManager: OidcUserManager; + + #authUrl = this.#config.authUrl; + #loginUrl = `${this.#authUrl}/rpc/login`; + #logoutUrl = `${this.#authUrl}/rpc/logout`; + #verifyTotpUrl = `${this.#authUrl}/rpc/verify-totp`; + #totpTokenUrl = `${this.#authUrl}/totp-token`; + #usersUrl = `${this.#authUrl}/users`; + + /** + * Get the current user session as a signal + * + * Null means that the user is not logged in (state is 'Anonymous'), while + * undefined means that the session has not yet been loaded (state 'Undetermined'). + */ + user = computed(() => this.#userSignal()); + + /** + * Check whether the session state is known as a signal + */ + isDetermined = computed(() => this.user() !== undefined); + + /** + * Check whether the session state is still loading as a signal + */ + isUndetermined = computed(() => this.user() === undefined); + + /** + * Check whether the user has been logged in with first factor as a signal + */ + isLoggedIn = computed(() => !!this.user()); + + /** + * Check whether the user has been logged out as a signal + */ + isLoggedOut = computed(() => this.user() === null); + + /** + * Check whether the user is fully authenticated with second factor as a signal + */ + isAuthenticated = computed(() => this.user()?.state == 'Authenticated'); + + /** + * Get the current user session state as a signal + */ + sessionState = computed(() => { + const state = this.user()?.state; + return state || (state === null ? 'LoggedOut' : 'Undetermined'); + }); + + /** + * Get the full name of the current user (with title) as a signal + */ + fullName = computed(() => this.user()?.full_name); + + /** + * Initialize the authentication service + */ + constructor() { + this.#oidcUserManager = new OidcUserManager(this.getOidcSettings()); + + OidcLog.setLogger(console); + OidcLog.setLevel(OidcLog.INFO); // set to DEBUG for more output + + /** + * On page load we must also load the user session from the server. + * We can only skip this when this is the callback page, since int + * this case the session is loaded with oidcRedirect(). + */ + if (window.location.pathname !== '/oauth/callback') { + this.#loadUserSession(); + } + } + + /** + * Gets the OIDC related settings + * + * These settings are derived from the application configuration + * and transformed into a UserManagerSettings object. + * @returns the settings as a UserManagerSettings object + */ + private getOidcSettings(): UserManagerSettings { + const config = this.#config; + + const settings: UserManagerSettings = { + authority: config.oidcAuthorityUrl, + client_id: config.oidcClientId, + redirect_uri: config.oidcRedirectUrl, + response_type: 'code', + scope: config.oidcScope, + loadUserInfo: false, + automaticSilentRenew: false, + }; + + const metadata: Partial = { + issuer: config.oidcAuthorityUrl, + authorization_endpoint: config.oidcAuthorizationUrl, + token_endpoint: config.oidcTokenUrl, + userinfo_endpoint: config.oidcUserInfoUrl, + }; + + /** + * If the "use_discovery" is true, we use the OIDC discovery mechanism + * to provide the metadata, and only seed it with the configured settings. + * Note that this requires an additional request and will only + * work if the origin header for a registered client is passed. + */ + if (config.oidcUseDiscovery) { + settings.metadataSeed = metadata; + } else { + settings.metadata = metadata; + } + + return settings; + } + + /** + * Initiate login via OIDC + * + * This returns a promise to trigger a redirect of the current window + * to the authorization endpoint of the OIDC provider. + */ + async login(): Promise { + this.#redirectAfterLogin = location.pathname; + this.#oidcUserManager.signinRedirect(); + } + + /** + * Handle OIDC redirect callback + * + * This method can be used as a canActivate guard for the OAuth callback route. + * @returns always false to prevent the route from being activated + */ + async oidcRedirect(): Promise { + if (await this.#oidcUserManager.getUser()) return false; // already logged in + let oidcUser: OidcUser | undefined; + let errorMessage: string | undefined; + try { + oidcUser = await this.#oidcUserManager.signinCallback(); + } catch (e) { + errorMessage = e instanceof Error ? e.message : String(e); + } + + if (oidcUser) { + if (!oidcUser.profile?.sub) { + errorMessage = 'No OpenID connect user'; + } else if (oidcUser.expired) { + errorMessage = 'OpenID connect login expired'; + } else if (!oidcUser.access_token) { + errorMessage = 'No OpenID connect access token'; + } + } + + if (!oidcUser || errorMessage) { + if (!errorMessage) { + errorMessage = 'OpenID connect login failed'; + } + console.error(errorMessage); // TODO: also add a toast message + return false; + } + + this.#loadUserSession(oidcUser.access_token); + return false; + } + + /** + * Login user via OIDC + * + * This returns a promise to trigger a redirect of the current window + * to the authorization endpoint of the OIDC provider. + */ + async logout(): Promise { + if (this.#userSignal()) { + firstValueFrom( + this.#http.post(this.#logoutUrl, null).pipe( + map(() => true), + catchError(() => of(false)), + ), + ).then(async () => { + await this.#oidcUserManager.removeUser(); + this.#userSignal.set(null); + this.#csrf.token = null; + this.#redirectAfterLogin = '/'; + this.#router.navigate(['/']); + }); + } + } + + /** + * Move session state + * @param state the new state to set + */ + #setNewState(state: LoginState): void { + this.#userSignal.update((user) => { + if (user && user.state !== state) { + user = { ...user, state }; + } + return user; + }); + } + + /** + * Get the deserialized user session from a JSON-formatted string. + * @param session the session as a JSON-formatted string or null + * @returns the parsed user or null if no session or undefined if error + */ + #parseUserFromSession(session: string): User | null | undefined { + if (!session) return null; + let user: User | null; + try { + user = JSON.parse(session || 'null'); + if (!user) return null; + if (!(user.ext_id && user.name && user.email)) { + throw new Error('Missing properties in user session'); + } + } catch (error) { + console.error('Cannot parse user session:', session, error); + return undefined; + } + if (!user.full_name) { + user.full_name = (user.title ? user.title + ' ' : '') + user.name; + } + return user; + } + + /** + * Load the current user session + * + * This method is called on startup to fetch the user session from the backend. + * + * It is also called to check when a state change was triggered and we need to + * wait for and verify that the backend has actually updated the user session. + * @param accessToken the access token to use for logging in + */ + async #loadUserSession(accessToken: string | undefined = undefined): Promise { + console.debug('Loading user session...'); + const userSignal = this.#userSignal; + let headers = new HttpHeaders(); + if (accessToken) headers = headers.set('X-Authorization', `Bearer ${accessToken}`); + this.#http + .post(this.#loginUrl, null, { observe: 'response', headers }) + .pipe( + // map to user or null if not found or undefined if error + map((response: HttpResponse) => { + if (response.status !== 204 || response.body) return undefined; + const session = response.headers.get('X-Session'); + if (!session) return undefined; + return this.#parseUserFromSession(session); + }), + catchError((error) => + of([401, 403, 404].includes(error?.status) ? null : undefined), + ), + ) + .subscribe((user: User | null | undefined) => { + if (user) { + console.debug('User session loaded:', user); + userSignal.set(user); + this.#csrf.token = user.csrf; + const router = this.#router; + switch (user.state) { + case 'NeedsRegistration': + case 'NeedsReRegistration': + router.navigate(['/register']); + break; + case 'Registered': + router.navigate(['/setup-totp']); + break; + case 'NewTotpToken': + case 'HasTotpToken': + router.navigate(['/confirm-totp']); + break; + } + } else { + if (accessToken || user === undefined) { + console.error('Failed to load user session'); + // TODO: also add a toast message + } + this.#oidcUserManager.removeUser(); + userSignal.set(user); + this.#csrf.token = null; + } + }); + } + + /** + * Wait for the backend user session to change the state + * @param state the state that is expected to change + * @param maxAttempts the maximum number of attempts to wait for the state + * @returns a promise that resolves to the last reached state + */ + async #waitForStateChange( + state: LoginState, + maxAttempts: number = 5, + ): Promise { + const newState = this.sessionState(); + if (newState !== state) return newState; + for (let attempt = 0; attempt < maxAttempts; attempt++) { + const wait = (1 << attempt) * 50; // exponential backoff + await new Promise((resolve) => setTimeout(resolve, wait)); + await this.#loadUserSession(); + const newState = this.sessionState(); + if (newState !== state) return newState; + } + return state; + } + + /** + * Register or re-register a user + * @param id the internal user ID or null for new users + * @param ext_id the external user ID (can be null for registered users) + * @param basicData the basic user data to register or re-register + * @returns a promise that resolves to true if the code is valid + */ + async register( + id: string | null, + ext_id: string | null, + basicData: UserBasicData, + ): Promise { + if (!id && !ext_id) return false; + const state = this.sessionState(); + let url = this.#usersUrl; + let rpc: Observable; + if (id) { + if (state !== 'NeedsReRegistration') return false; + rpc = this.#http.put(`${url}/${id}`, basicData); + } else { + if (state !== 'NeedsRegistration') return false; + if (!ext_id) return false; + rpc = this.#http.post(url, { ...basicData, ext_id }); + } + await firstValueFrom(rpc); + const newState = await this.#waitForStateChange(state); + return ( + newState != state && + !['LoggedOut', 'LoggedIn', 'NeedsRegistration', 'NeedsReRegistration'].includes( + newState, + ) + ); + } + + /** + * Create a TOTP token + * @returns a promise that resolves to the provisioning URI or null + */ + async createTotpToken(): Promise { + const state = this.sessionState(); + if (!['NeedsTotpToken', 'LostTotpToken'].includes(state)) return null; + let params = new HttpParams(); + if (state == 'LostTotpToken') { + params = params.set('force', 'true'); + } + return firstValueFrom( + this.#http.post<{ uri: string }>(this.#totpTokenUrl, null, { params }).pipe( + map(({ uri }) => { + console.info('TOTP token created'); + return uri || null; + }), + catchError(() => { + console.error('Failed to create TOTP token'); + return of(null); + }), + ), + ); + } + + /** + * Move state to "needs TOTP token" and continue with setup + */ + needsTotpSetup(): void { + this.#setNewState('NeedsTotpToken'); + } + + /** + * Move state to "lost TOTP token" and proceed with setup + */ + lostTotpSetup(): void { + this.#setNewState('LostTotpToken'); + this.#router.navigate(['/setup-totp']); + } + + /** + * Move state to "has TOTP token" and proceed with confirmation + */ + completeTotpSetup(): void { + this.#setNewState('HasTotpToken'); + this.#router.navigate(['/confirm-totp']); + } + + /** + * Verify the given TOTP code + * @param code the 6-digit TOTP code to verify + * @returns a promise that resolves to true if the code is valid + */ + async verifyTotpCode(code: string): Promise { + const state = this.sessionState(); + if (!['NewTotpToken', 'HasTotpToken'].includes(state)) return false; + let headers = new HttpHeaders(); + headers = headers.set('X-Authorization', `Bearer TOTP:${code}`); + if (!code || code.length !== 6 || !/^\d{6}$/.test(code)) { + console.error('Invalid TOTP code'); + return false; + } + return firstValueFrom( + this.#http.post(this.#verifyTotpUrl, null, { headers }).pipe( + map(() => { + console.info('TOTP code verified'); + this.#setNewState('Authenticated'); + return true; + }), + catchError(() => { + console.error('Failed to verify TOTP code'); + return of(false); + }), + ), + ); + } + + /** + * Redirect back to the original page after login + */ + redirectAfterLogin() { + this.#router.navigate([this.#redirectAfterLogin]); + } +} diff --git a/src/app/auth/services/csrf.service.ts b/src/app/auth/services/csrf.service.ts new file mode 100644 index 0000000..57b3e9d --- /dev/null +++ b/src/app/auth/services/csrf.service.ts @@ -0,0 +1,46 @@ +import { + HttpEvent, + HttpHandler, + HttpInterceptor, + HttpRequest, +} from '@angular/common/http'; +import { inject, Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; + +/** + * A tiny service that just holds the CSRF token + * + * This is used by both the auth service and the CSRF interceptor. + */ +@Injectable({ providedIn: 'root' }) +export class CsrfService { + token: string | null = null; +} + +/** + * Custom CSRF Interceptor + */ +@Injectable() +export class CsrfInterceptor implements HttpInterceptor { + #csrf = inject(CsrfService); + + /** + * Intercept all HTTP requests and add the CSRF token if needed + * @param req the outgoing request object to handle + * @param next the next interceptor in the chain + * @returns an observable of the HTTP event + */ + intercept( + req: HttpRequest, + next: HttpHandler, + ): Observable> { + const method = req.method; + if (method && /^(POST|PUT|PATCH|DELETE)$/.test(method)) { + const csrfToken = this.#csrf.token; + if (csrfToken) { + req = req.clone({ setHeaders: { 'X-CSRF-Token': csrfToken } }); + } + } + return next.handle(req); + } +} diff --git a/src/app/portal/features/home-page/home-page.component.html b/src/app/portal/features/home-page/home-page.component.html index ce85e00..68c39d9 100644 --- a/src/app/portal/features/home-page/home-page.component.html +++ b/src/app/portal/features/home-page/home-page.component.html @@ -15,7 +15,3 @@ Infrastructure initiative (NFDI) and by the contributing institutions.
More at www.ghga.de.

-

- Just to show that we can access the config, this is the MASS URL: - {{ massUrl }} -

diff --git a/src/app/portal/features/home-page/home-page.component.ts b/src/app/portal/features/home-page/home-page.component.ts index 22dc4b0..0362110 100644 --- a/src/app/portal/features/home-page/home-page.component.ts +++ b/src/app/portal/features/home-page/home-page.component.ts @@ -1,5 +1,4 @@ -import { Component, inject } from '@angular/core'; -import { ConfigService } from '@app/shared/services/config.service'; +import { Component } from '@angular/core'; /** * This is the home page component @@ -9,8 +8,4 @@ import { ConfigService } from '@app/shared/services/config.service'; imports: [], templateUrl: './home-page.component.html', }) -export class HomePageComponent { - #config = inject(ConfigService); - - massUrl = this.#config.massUrl; // just to show that we can access the config service -} +export class HomePageComponent {} diff --git a/src/app/portal/features/show-session/show-session.component.html b/src/app/portal/features/show-session/show-session.component.html new file mode 100644 index 0000000..13c75e1 --- /dev/null +++ b/src/app/portal/features/show-session/show-session.component.html @@ -0,0 +1,5 @@ +

+ Developer note: Currently logged in as + {{ fullName() || 'Guest' }} + in session state {{ sessionState() }} . +

diff --git a/src/app/portal/features/show-session/show-session.component.spec.ts b/src/app/portal/features/show-session/show-session.component.spec.ts new file mode 100644 index 0000000..42a5ba9 --- /dev/null +++ b/src/app/portal/features/show-session/show-session.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ShowSessionComponent } from './show-session.component'; + +describe('ShowSessionComponent', () => { + let component: ShowSessionComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ShowSessionComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(ShowSessionComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/portal/features/show-session/show-session.component.ts b/src/app/portal/features/show-session/show-session.component.ts new file mode 100644 index 0000000..d0c5f16 --- /dev/null +++ b/src/app/portal/features/show-session/show-session.component.ts @@ -0,0 +1,19 @@ +import { Component, inject } from '@angular/core'; +import { AuthService } from '@app/auth/services/auth.service'; + +/** + * Show user session information + * + * TODO: Only used for testing, remove this component later! + */ +@Component({ + selector: 'app-show-session', + imports: [], + templateUrl: './show-session.component.html', +}) +export class ShowSessionComponent { + #authService = inject(AuthService); + + fullName = this.#authService.fullName; + sessionState = this.#authService.sessionState; +} diff --git a/src/app/portal/features/site-header/site-header.component.html b/src/app/portal/features/site-header/site-header.component.html index 5e7fe0b..7dc2a5c 100644 --- a/src/app/portal/features/site-header/site-header.component.html +++ b/src/app/portal/features/site-header/site-header.component.html @@ -2,8 +2,12 @@ GHGA Logo - HomeBrowse - Login Button + Home + Browse + + @if (isLoggedIn()) { + + } @else { + + } diff --git a/src/app/portal/features/site-header/site-header.component.ts b/src/app/portal/features/site-header/site-header.component.ts index 689a8dc..b06c686 100644 --- a/src/app/portal/features/site-header/site-header.component.ts +++ b/src/app/portal/features/site-header/site-header.component.ts @@ -1,7 +1,9 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; import { MatNavList } from '@angular/material/list'; import { MatToolbarModule } from '@angular/material/toolbar'; import { RouterLink } from '@angular/router'; +import { AuthService } from '@app/auth/services/auth.service'; /** * This is the site header component @@ -10,6 +12,24 @@ import { RouterLink } from '@angular/router'; selector: 'app-site-header', templateUrl: './site-header.component.html', standalone: true, - imports: [MatToolbarModule, MatNavList, RouterLink], + imports: [MatToolbarModule, MatNavList, MatButtonModule, RouterLink], }) -export class SiteHeaderComponent {} +export class SiteHeaderComponent { + #authService = inject(AuthService); + + isLoggedIn = this.#authService.isLoggedIn; + + /** + * User login + */ + onLogin(): void { + this.#authService.login(); + } + + /** + * User logout + */ + onLogout(): void { + this.#authService.logout(); + } +} diff --git a/src/app/shared/services/config.service.ts b/src/app/shared/services/config.service.ts index 80320f9..6054ddb 100644 --- a/src/app/shared/services/config.service.ts +++ b/src/app/shared/services/config.service.ts @@ -1,8 +1,18 @@ import { Injectable } from '@angular/core'; interface Config { + base_url: string; + auth_url: string; mass_url: string; metldata_url: string; + oidc_client_id: string; + oidc_redirect_url: string; + oidc_scope: string; + oidc_authority_url: string; + oidc_authorization_url: string; + oidc_token_url: string; + oidc_userinfo_url: string; + oidc_use_discovery: boolean; } declare global { @@ -11,6 +21,24 @@ declare global { } } +/** + * Removes an end slash to a URL if needed + * @param url - a URL that might not end with a slash + * @returns the URL without an end slash + */ +function sansEndSlash(url: string): string { + return url.endsWith('/') ? url.slice(0, -1) : url; +} + +/** + * Adds an end slash to a URL if needed + * @param url - a URL that might not end with a slash + * @returns the URL with an end slash + */ +function withEndSlash(url: string): string { + return url.endsWith('/') ? url : url + '/'; +} + /** * The config service provides access to the configuration of the application. */ @@ -26,20 +54,104 @@ export class ConfigService { constructor() { this.#config = window.config; } + /** + * Gets the application base URL from the config object + * @returns the application base URL sans end slash + */ + get baseUrl(): string { + return sansEndSlash(this.#config.base_url); + } + + /** + * Gets the auth service URL from the config object. + * @returns the auth service URL sans end slash + */ + get authUrl(): string { + return new URL(this.#config.auth_url, withEndSlash(this.#config.base_url)).href; + } /** - * Gets the mass url from the config object. - * @returns the mass url as a string + * Gets the MASS URL from the config object. + * @returns the MASS URL sans end slash */ get massUrl(): string { - return this.#config.mass_url; + return new URL(this.#config.mass_url, withEndSlash(this.#config.base_url)).href; } /** - * Gets the metldataUrl from the config object. - * @returns the metldataUrl as a string + * Gets the metldata service URL from the config object + * @returns the metldata service URL sans slash */ get metldataUrl(): string { - return this.#config.metldata_url; + return new URL(this.#config.metldata_url, withEndSlash(this.#config.base_url)).href; + } + + /** + * Gets the OIDC client ID from the config object + * @returns the OIDC client ID + */ + get oidcClientId(): string { + return this.#config.oidc_client_id; + } + + /** + * Gets the OIDC redirect URL from the config object + * @returns the OIDC redirect URL + */ + get oidcRedirectUrl(): string { + return new URL(this.#config.oidc_redirect_url, withEndSlash(this.baseUrl)).href; + } + + /** + * Gets the OIDC scope from the config object + * @returns the OIDC scope + */ + get oidcScope(): string { + return this.#config.oidc_scope; + } + + /** + * Gets the OIDC authority URL from the config object + * @returns the OIDC authority URL + */ + get oidcAuthorityUrl(): string { + return this.#config.oidc_authority_url; + } + + /** + * Gets the OIDC authorization URL from the config object + * @returns the OIDC authorization URL + */ + get oidcAuthorizationUrl(): string { + return new URL( + this.#config.oidc_authorization_url, + withEndSlash(this.oidcAuthorityUrl), + ).href; + } + + /** + * Gets the OIDC token URL from the config object + * @returns the OIDC token URL + */ + get oidcTokenUrl(): string { + return new URL(this.#config.oidc_token_url, withEndSlash(this.oidcAuthorityUrl)) + .href; + } + + /** + * Gets the OIDC userinfo URL from the config object + * @returns the OIDC userinfo URL + */ + get oidcUserInfoUrl(): string { + return new URL(this.#config.oidc_userinfo_url, withEndSlash(this.oidcAuthorityUrl)) + .href; + } + + /** + * Checks whether OIDC discovery shall be used + * @returns true if OIDC discovery shall be used + */ + get oidcUseDiscovery(): boolean { + return this.#config.oidc_use_discovery; } } diff --git a/tsconfig.json b/tsconfig.json index 95c6439..20bacef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,12 +22,13 @@ "@app/*": [ "src/app/*" ] - } + }, }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, "strictInjectionParameters": true, "strictInputAccessModifiers": true, + "strictStandalone": true, "strictTemplates": true } } \ No newline at end of file