From 3f47900f06d970fdf2a15268a39b2520e28d4563 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Dec 2025 04:53:32 +0000 Subject: [PATCH 1/8] Chore(deps): Bump actions/checkout from 5 to 6 in the actions group (#11515) Bumps the actions group with 1 update: [actions/checkout](https://github.com/actions/checkout). Updates `actions/checkout` from 5 to 6 - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major dependency-group: actions ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 20 ++++++++++---------- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/crowdin.yml | 2 +- .github/workflows/translate-strings.yml | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1cc4c3ce..3c567fbef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Install python uses: actions/setup-python@v6 with: @@ -81,7 +81,7 @@ jobs: - pre-commit steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Python id: setup-python uses: actions/setup-python@v6 @@ -131,7 +131,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Start containers run: | docker compose --file ${{ github.workspace }}/docker/compose/docker-compose.ci-test.yml pull --quiet @@ -202,7 +202,7 @@ jobs: needs: - pre-commit steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install pnpm uses: pnpm/action-setup@v4 with: @@ -235,7 +235,7 @@ jobs: shard-index: [1, 2, 3, 4] shard-count: [4] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install pnpm uses: pnpm/action-setup@v4 with: @@ -284,7 +284,7 @@ jobs: shard-index: [1, 2] shard-count: [2] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install pnpm uses: pnpm/action-setup@v4 with: @@ -327,7 +327,7 @@ jobs: - tests-frontend - tests-frontend-e2e steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Install pnpm uses: pnpm/action-setup@v4 with: @@ -424,7 +424,7 @@ jobs: type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 # If https://github.com/docker/buildx/issues/1044 is resolved, # the append input with a native arm64 arch could be used to # significantly speed up building @@ -497,7 +497,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Set up Python id: setup-python uses: actions/setup-python@v6 @@ -643,7 +643,7 @@ jobs: if: needs.publish-release.outputs.prerelease == 'false' steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: ref: main - name: Set up Python diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 77913ba81..941d83648 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,7 +34,7 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index b4b4cd2ac..29fb4c181 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: token: ${{ secrets.PNGX_BOT_PAT }} - name: crowdin action diff --git a/.github/workflows/translate-strings.yml b/.github/workflows/translate-strings.yml index a3326cbcf..bd9eafae5 100644 --- a/.github/workflows/translate-strings.yml +++ b/.github/workflows/translate-strings.yml @@ -11,7 +11,7 @@ jobs: contents: write steps: - name: Checkout code - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: token: ${{ secrets.PNGX_BOT_PAT }} ref: ${{ github.head_ref }} From 8efc998687122ee54c2b94844321f09c38e93ddb Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 8 Dec 2025 15:53:10 -0800 Subject: [PATCH 2/8] Chore: refactor permission checks to use queryset.exists() --- src/documents/permissions.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/documents/permissions.py b/src/documents/permissions.py index cf6a9aa35..802cb8798 100644 --- a/src/documents/permissions.py +++ b/src/documents/permissions.py @@ -85,12 +85,12 @@ def set_permissions_for_object(permissions: list[str], object, *, merge: bool = if not merge else User.objects.none() ) - if len(users_to_add) > 0 and len(users_to_remove) > 0: + if users_to_add.exists() and users_to_remove.exists(): users_to_remove = users_to_remove.exclude(id__in=users_to_add) - if len(users_to_remove) > 0: + if users_to_remove.exists(): for user in users_to_remove: remove_perm(permission, user, object) - if len(users_to_add) > 0: + if users_to_add.exists(): for user in users_to_add: assign_perm(permission, user, object) if action == "change": @@ -111,12 +111,12 @@ def set_permissions_for_object(permissions: list[str], object, *, merge: bool = if not merge else Group.objects.none() ) - if len(groups_to_add) > 0 and len(groups_to_remove) > 0: + if groups_to_add.exists() and groups_to_remove.exists(): groups_to_remove = groups_to_remove.exclude(id__in=groups_to_add) - if len(groups_to_remove) > 0: + if groups_to_remove.exists(): for group in groups_to_remove: remove_perm(permission, group, object) - if len(groups_to_add) > 0: + if groups_to_add.exists(): for group in groups_to_add: assign_perm(permission, group, object) if action == "change": From f3fc3febf173e15e6b38957c6ac16f7c485abf03 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 9 Dec 2025 12:15:49 -0800 Subject: [PATCH 3/8] Chore: update Angular dependencies to 20.3.15 (#11568) --- src-ui/package.json | 26 +- src-ui/pnpm-lock.yaml | 579 +++++++++++++++++++++--------------------- 2 files changed, 305 insertions(+), 300 deletions(-) diff --git a/src-ui/package.json b/src-ui/package.json index f3d04a628..c0ff9dea6 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -12,14 +12,14 @@ "private": true, "dependencies": { "@angular/cdk": "^20.2.13", - "@angular/common": "~20.3.14", - "@angular/compiler": "~20.3.12", - "@angular/core": "~20.3.12", - "@angular/forms": "~20.3.12", - "@angular/localize": "~20.3.12", - "@angular/platform-browser": "~20.3.12", - "@angular/platform-browser-dynamic": "~20.3.12", - "@angular/router": "~20.3.12", + "@angular/common": "~20.3.15", + "@angular/compiler": "~20.3.15", + "@angular/core": "~20.3.15", + "@angular/forms": "~20.3.15", + "@angular/localize": "~20.3.15", + "@angular/platform-browser": "~20.3.15", + "@angular/platform-browser-dynamic": "~20.3.15", + "@angular/router": "~20.3.15", "@ng-bootstrap/ng-bootstrap": "^19.0.1", "@ng-select/ng-select": "^20.7.0", "@ngneat/dirty-check-forms": "^3.0.3", @@ -42,16 +42,16 @@ "devDependencies": { "@angular-builders/custom-webpack": "^20.0.0", "@angular-builders/jest": "^20.0.0", - "@angular-devkit/core": "^20.3.10", - "@angular-devkit/schematics": "^20.3.10", + "@angular-devkit/core": "^20.3.13", + "@angular-devkit/schematics": "^20.3.13", "@angular-eslint/builder": "20.6.0", "@angular-eslint/eslint-plugin": "20.6.0", "@angular-eslint/eslint-plugin-template": "20.6.0", "@angular-eslint/schematics": "20.6.0", "@angular-eslint/template-parser": "20.6.0", - "@angular/build": "^20.3.10", - "@angular/cli": "~20.3.10", - "@angular/compiler-cli": "~20.3.12", + "@angular/build": "^20.3.13", + "@angular/cli": "~20.3.13", + "@angular/compiler-cli": "~20.3.15", "@codecov/webpack-plugin": "^1.9.1", "@playwright/test": "^1.57.0", "@types/jest": "^30.0.0", diff --git a/src-ui/pnpm-lock.yaml b/src-ui/pnpm-lock.yaml index e86f48c8d..a57b78864 100644 --- a/src-ui/pnpm-lock.yaml +++ b/src-ui/pnpm-lock.yaml @@ -10,40 +10,40 @@ importers: dependencies: '@angular/cdk': specifier: ^20.2.13 - version: 20.2.13(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + version: 20.2.13(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': - specifier: ~20.3.14 - version: 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: ~20.3.15 + version: 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/compiler': - specifier: ~20.3.12 - version: 20.3.12 + specifier: ~20.3.15 + version: 20.3.15 '@angular/core': - specifier: ~20.3.12 - version: 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + specifier: ~20.3.15 + version: 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) '@angular/forms': - specifier: ~20.3.12 - version: 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: ~20.3.15 + version: 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/localize': - specifier: ~20.3.12 - version: 20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12) + specifier: ~20.3.15 + version: 20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15) '@angular/platform-browser': - specifier: ~20.3.12 - version: 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: ~20.3.15 + version: 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-browser-dynamic': - specifier: ~20.3.12 - version: 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))) + specifier: ~20.3.15 + version: 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))) '@angular/router': - specifier: ~20.3.12 - version: 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: ~20.3.15 + version: 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@ng-bootstrap/ng-bootstrap': specifier: ^19.0.1 - version: 19.0.1(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@popperjs/core@2.11.8)(rxjs@7.8.2) + version: 19.0.1(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': specifier: ^20.7.0 - version: 20.7.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)) + version: 20.7.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)) '@ngneat/dirty-check-forms': specifier: ^3.0.3 - version: 3.0.3(8ff1ffec9c0eb3e42a8b58cc79f67aaa) + version: 3.0.3(218c118fbd72b8c5b4f5fd1fa1211ac5) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -61,19 +61,19 @@ importers: version: 10.4.0 ngx-bootstrap-icons: specifier: ^1.9.3 - version: 1.9.3(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + version: 1.9.3(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) ngx-color: specifier: ^10.1.0 - version: 10.1.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + version: 10.1.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) ngx-cookie-service: specifier: ^20.1.1 - version: 20.1.1(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + version: 20.1.1(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) ngx-device-detector: specifier: ^10.1.0 - version: 10.1.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + version: 10.1.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) ngx-ui-tour-ng-bootstrap: specifier: ^17.0.1 - version: 17.0.1(1990d937b48b356c3a7b81a3382c7bae) + version: 17.0.1(43e0b240967005bbda3e3b71e72070f1) rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -92,16 +92,16 @@ importers: devDependencies: '@angular-builders/custom-webpack': specifier: ^20.0.0 - version: 20.0.0(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) + version: 20.0.0(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) '@angular-builders/jest': specifier: ^20.0.0 - version: 20.0.0(2a2dc918d42153e5c2c5a3eb18182c36) + version: 20.0.0(6f21bd249f8e5dee2ad8e6487b28db46) '@angular-devkit/core': - specifier: ^20.3.10 - version: 20.3.10(chokidar@4.0.3) + specifier: ^20.3.13 + version: 20.3.13(chokidar@4.0.3) '@angular-devkit/schematics': - specifier: ^20.3.10 - version: 20.3.10(chokidar@4.0.3) + specifier: ^20.3.13 + version: 20.3.13(chokidar@4.0.3) '@angular-eslint/builder': specifier: 20.6.0 version: 20.6.0(chokidar@4.0.3)(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) @@ -118,14 +118,14 @@ importers: specifier: 20.6.0 version: 20.6.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) '@angular/build': - specifier: ^20.3.10 - version: 20.3.10(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) + specifier: ^20.3.13 + version: 20.3.13(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) '@angular/cli': - specifier: ~20.3.10 - version: 20.3.10(@types/node@24.10.1)(chokidar@4.0.3) + specifier: ~20.3.13 + version: 20.3.13(@types/node@24.10.1)(chokidar@4.0.3) '@angular/compiler-cli': - specifier: ~20.3.12 - version: 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + specifier: ~20.3.15 + version: 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) '@codecov/webpack-plugin': specifier: ^1.9.1 version: 1.9.1(webpack@5.103.0) @@ -161,7 +161,7 @@ importers: version: 16.0.0 jest-preset-angular: specifier: ^15.0.3 - version: 15.0.3(138e950b6256ba9944139a8c5aad3bdf) + version: 15.0.3(e164a15bea80c603eb581d5724a352ec) jest-websocket-mock: specifier: ^2.5.0 version: 2.5.0 @@ -283,6 +283,10 @@ packages: resolution: {integrity: sha512-2SWetxJzS8gRX6OKQstkWx37VRvZVgcEBDLsDSaeTjpnwh81A+niZQjAVRdwL0NEt1Wixk/RxfeUuCmdyyHvhQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular-devkit/architect@0.2003.13': + resolution: {integrity: sha512-JyH6Af6PNC1IHJToColFk1RaXDU87mpPjz7M5sWDfn8bC+KBipw6dSdRkCEuw0D9HY1lZkC9EBV9k9GhpvHjCQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + '@angular-devkit/build-angular@20.0.4': resolution: {integrity: sha512-YUf9hRAd//yu44vGMnET1ajmUMXwSz0t4rOajDj5yb57sYS9eYu912K2pWfDNDNJncOshtpklvBqUDngDNcPDw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} @@ -358,8 +362,17 @@ packages: chokidar: optional: true - '@angular-devkit/schematics@20.3.10': - resolution: {integrity: sha512-2N2WF9lj+kr3uCG4+vFadYCL5hAT4dxMgzwScSdOqSd0O+GZD0CzKbDzlfvWIWC/ZealC5Sh4dFEQaRfmy72xA==} + '@angular-devkit/core@20.3.13': + resolution: {integrity: sha512-/D84T1Caxll3I2sRihPDR9UaWBhF50M+tAX15PdP6uSh/TxwAlLl9p7Rm1bD0mPjPercqaEKA+h9a9qLP16hug==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + peerDependencies: + chokidar: ^4.0.0 + peerDependenciesMeta: + chokidar: + optional: true + + '@angular-devkit/schematics@20.3.13': + resolution: {integrity: sha512-hdMKY4rUTko8xqeWYGnwwDYDomkeOoLsYsP6SdaHWK7hpGvzWsT6Q/aIv8J8NrCYkLu+M+5nLiKOooweUZu3GQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@angular-eslint/builder@20.6.0': @@ -449,8 +462,8 @@ packages: vitest: optional: true - '@angular/build@20.3.10': - resolution: {integrity: sha512-nQrj1nMNZygYDilThc7hPrD6/NIWF/BOSgMfE4VkXQp8d0QronP3HFJ/h77MeoughMRFRhix0pqQSlXJQ2SGTQ==} + '@angular/build@20.3.13': + resolution: {integrity: sha512-/5pM3ZS+lLkZgA+n6TMmNV8I6t9Ow1C6Vkj6bXqWeOgFDH5LwnIEZFAKzEDBkCGos0m2gPKPcREcDD5tfp9h4g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: '@angular/compiler': ^20.0.0 @@ -460,7 +473,7 @@ packages: '@angular/platform-browser': ^20.0.0 '@angular/platform-server': ^20.0.0 '@angular/service-worker': ^20.0.0 - '@angular/ssr': ^20.3.10 + '@angular/ssr': ^20.3.13 karma: ^6.4.0 less: ^4.2.0 ng-packagr: ^20.0.0 @@ -502,38 +515,38 @@ packages: '@angular/core': ^20.0.0 || ^21.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/cli@20.3.10': - resolution: {integrity: sha512-CQzXScurBXSuMMn0jf6UYDItdggaM3bHYERKL4cUG1z5JqSozVFin1+TB1EjWYkddwdgC10R5xQurdMb+ahRNw==} + '@angular/cli@20.3.13': + resolution: {integrity: sha512-G78I/HDJULloS2LSqfUfbmBlhDCbcWujIRWfuMnGsRf82TyGA2OEPe3IA/F8MrJfeOzPQim2fMyn24MqHL40Vg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@20.3.14': - resolution: {integrity: sha512-OOUvjTtnpktJLsNupA+GFT2q5zNocPdpOENA8aSrXvAheNybLjgi+otO3U3sQsvB1VwaoEZ9GT5O3lZlstnA/A==} + '@angular/common@20.3.15': + resolution: {integrity: sha512-k4mCXWRFiOHK3bUKfWkRQQ8KBPxW8TAJuKLYCsSHPCpMz6u0eA1F0VlrnOkZVKWPI792fOaEAWH2Y4PTaXlUHw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 20.3.14 + '@angular/core': 20.3.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@20.3.12': - resolution: {integrity: sha512-3SJkexqsydYjIs0iLiJr5AdwkvumpzvjJM6s76iaxXHkRll5k/vM0wqkXLlSIwieBrecO9D4J73lDLWDevXl5A==} + '@angular/compiler-cli@20.3.15': + resolution: {integrity: sha512-8sJoxodxsfyZ8eJ5r6Bx7BCbazXYgsZ1+dE8t5u5rTQ6jNggwNtYEzkyReoD5xvP+MMtRkos3xpwq4rtFnpI6A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 20.3.12 + '@angular/compiler': 20.3.15 typescript: '>=5.8 <6.0' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@20.3.12': - resolution: {integrity: sha512-bGESKz97nWiEQ/sydTq/Lzv3zlLvDb8t0msLG5Xti7Ch1EdLddXS8d2D/zFsjiGbAUKVsT6RgPCLHYoi4ocbhA==} + '@angular/compiler@20.3.15': + resolution: {integrity: sha512-lMicIAFAKZXa+BCZWs3soTjNQPZZXrF/WMVDinm8dQcggNarnDj4UmXgKSyXkkyqK5SLfnLsXVzrX6ndVT6z7A==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@20.3.12': - resolution: {integrity: sha512-K7vibMr55a7+EsuDhkg4Pk+ELuMm12olllwqL/CiQUcHXZ9Zgc4KYGTUuxWB69qJCG90gdSZS7tm5Dx0wDcyjg==} + '@angular/core@20.3.15': + resolution: {integrity: sha512-NMbX71SlTZIY9+rh/SPhRYFJU0pMJYW7z/TBD4lqiO+b0DTOIg1k7Pg9ydJGqSjFO1Z4dQaA6TteNuF99TJCNw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 20.3.12 + '@angular/compiler': 20.3.15 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 peerDependenciesMeta: @@ -542,50 +555,50 @@ packages: zone.js: optional: true - '@angular/forms@20.3.12': - resolution: {integrity: sha512-O0Jy8ScaN3qVipDfR4s0SIxGrz/+MbCdmR05ZYVWf1W5P3dvETKt9WNjX9fYYV47GdgSveyFjuCR2NvWlv94zA==} + '@angular/forms@20.3.15': + resolution: {integrity: sha512-gS5hQkinq52pm/7mxz4yHPCzEcmRWjtUkOVddPH0V1BW/HMni/p4Y6k2KqKBeGb9p8S5EAp6PDxDVLOPukp3mg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 20.3.12 - '@angular/core': 20.3.12 - '@angular/platform-browser': 20.3.12 + '@angular/common': 20.3.15 + '@angular/core': 20.3.15 + '@angular/platform-browser': 20.3.15 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@20.3.12': - resolution: {integrity: sha512-wolRAeaWCh6kLNZitrlnQYm9nPaGQ2OwO04I10p1dEY2gC/nCMdJvh3umaOHTD2lN64ebZUxS5gJS8+PPTOcmg==} + '@angular/localize@20.3.15': + resolution: {integrity: sha512-vJDLXzQgLE+zpzwT2n85yWmWHuk9BsnPByOHyF63K178GFs0TG49UqZOFKohGbq5Vlo1GI9NIvXkcegkoMJCMQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 20.3.12 - '@angular/compiler-cli': 20.3.12 + '@angular/compiler': 20.3.15 + '@angular/compiler-cli': 20.3.15 - '@angular/platform-browser-dynamic@20.3.12': - resolution: {integrity: sha512-VviTUCpcbwErQjWd+EZklQf1Fw1FtXui6ey4rEb9g9mCEJ/o08LkM7mWV5IoE6QNCfbgkfgNjEJSJvWe409Mow==} + '@angular/platform-browser-dynamic@20.3.15': + resolution: {integrity: sha512-RizuRdBt0d6ongQ2y8cr8YsXFyjF8f91vFfpSNw+cFj+oiEmRC1txcWUlH5bPLD9qSDied8qazUi0Tb8VPQDGw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 20.3.12 - '@angular/compiler': 20.3.12 - '@angular/core': 20.3.12 - '@angular/platform-browser': 20.3.12 + '@angular/common': 20.3.15 + '@angular/compiler': 20.3.15 + '@angular/core': 20.3.15 + '@angular/platform-browser': 20.3.15 - '@angular/platform-browser@20.3.12': - resolution: {integrity: sha512-14KQsXZyaQhbRwFz1W58CtbXQc9L+mfuHBgwQjQo99422Yk0ye5WVMb6DHH7dH671qFVqL0XL7zdOPBebaAnJQ==} + '@angular/platform-browser@20.3.15': + resolution: {integrity: sha512-TxRM/wTW/oGXv/3/Iohn58yWoiYXOaeEnxSasiGNS1qhbkcKtR70xzxW6NjChBUYAixz2ERkLURkpx3pI8Q6Dw==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 20.3.12 - '@angular/common': 20.3.12 - '@angular/core': 20.3.12 + '@angular/animations': 20.3.15 + '@angular/common': 20.3.15 + '@angular/core': 20.3.15 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@20.3.12': - resolution: {integrity: sha512-hUipb9JI/Euy3bdlhzkcWlw3cTyssPTVTDwSvyGxWO4i+UKATQYmxh8EDOrDYzFp6Aexiy0Hff/H8umdsn6ZdA==} + '@angular/router@20.3.15': + resolution: {integrity: sha512-6+qgk8swGSoAu7ISSY//GatAyCP36hEvvUgvjbZgkXLLH9yUQxdo77ij05aJ5s0OyB25q/JkqS8VTY0z1yE9NQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 20.3.12 - '@angular/core': 20.3.12 - '@angular/platform-browser': 20.3.12 + '@angular/common': 20.3.15 + '@angular/core': 20.3.15 + '@angular/platform-browser': 20.3.15 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@3.2.0': @@ -2200,9 +2213,15 @@ packages: cpu: [x64] os: [win32] - '@modelcontextprotocol/sdk@1.17.3': - resolution: {integrity: sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==} + '@modelcontextprotocol/sdk@1.24.0': + resolution: {integrity: sha512-D8h5KXY2vHFW8zTuxn2vuZGN0HGrQ5No6LkHwlEA9trVgNdPL3TF1dSqKA7Dny6BbBYKSW/rOBDXdC8KJAjUCg==} engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} @@ -2783,8 +2802,8 @@ packages: cpu: [x64] os: [win32] - '@schematics/angular@20.3.10': - resolution: {integrity: sha512-F9ntS2CElpoWlENf4b03nwdTcN9Ri0Nb4SAE/pfRw3In09h2UHxYyf1ex9jqQt70xltDg4wvyuc3mMs+JlSx9A==} + '@schematics/angular@20.3.13': + resolution: {integrity: sha512-ETJ1budKmrkdxojo5QP6TPr6zQZYGxtWWf8NrX1cBIS851zPCmFkKyhSFLZsoksariYF/LP8ljvm8tlcIzt/XA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@3.1.0': @@ -3404,10 +3423,6 @@ packages: base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.8.29: - resolution: {integrity: sha512-sXdt2elaVnhpDNRDz+1BDx1JQoJRuNk7oVlAlbGiFkLikHCAQiccexF/9e91zVi6RCgqspl04aP+6Cnl9zRLrA==} - hasBin: true - baseline-browser-mapping@2.9.4: resolution: {integrity: sha512-ZCQ9GEWl73BVm8bu5Fts8nt7MHdbt5vY9bP6WGnUh+r3l8M7CgfyTlwsgCbMC66BNxPr6Xoce3j66Ms5YUQTNA==} hasBin: true @@ -3465,11 +3480,6 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.0: - resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} - engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} - hasBin: true - browserslist@4.28.1: resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -3520,9 +3530,6 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001755: - resolution: {integrity: sha512-44V+Jm6ctPj7R52Na4TLi3Zri4dWUljJd+RDm+j8LtNCc/ihLCT+X1TzoOAkRETEWqjuLnh9581Tl80FvK7jVA==} - caniuse-lite@1.0.30001759: resolution: {integrity: sha512-Pzfx9fOKoKvevQf8oCXoyNRQ5QyxJj+3O0Rqx2V5oxT61KGx8+n6hV/IUyJeifUci2clnmmKVpvtiqRzgiWjSw==} @@ -3905,9 +3912,6 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.255: - resolution: {integrity: sha512-Z9oIp4HrFF/cZkDPMpz2XSuVpc1THDpT4dlmATFlJUIBVCy9Vap5/rIXsASP1CscBacBqhabwh8vLctqBwEerQ==} - electron-to-chromium@1.5.266: resolution: {integrity: sha512-kgWEglXvkEfMH7rxP5OSZZwnaDWT7J9EoZCujhnpLbfi0bbNtRkgdX2E3gt0Uer11c61qCYktB3hwkAS325sJg==} @@ -4899,6 +4903,9 @@ packages: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true + jose@6.1.3: + resolution: {integrity: sha512-0TpaTfihd4QMNwrz/ob2Bp7X04yuxJkjRGi4aKmOqwhov54i6u79oCv7T+C7lo70MKH6BesI3vscD1yb/yzKXQ==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -6556,12 +6563,6 @@ packages: unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} - update-browserslist-db@1.1.4: - resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' - update-browserslist-db@1.2.2: resolution: {integrity: sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==} hasBin: true @@ -6930,14 +6931,17 @@ packages: resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} engines: {node: '>=18'} - zod-to-json-schema@3.24.6: - resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + zod-to-json-schema@3.25.0: + resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} peerDependencies: - zod: ^3.24.1 + zod: ^3.25 || ^4 zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@4.1.13: + resolution: {integrity: sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==} + zone.js@0.15.1: resolution: {integrity: sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==} @@ -7060,7 +7064,7 @@ snapshots: '@angular-builders/common@4.0.0(@types/node@24.10.1)(chokidar@4.0.3)(typescript@5.8.3)': dependencies: - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) ts-node: 10.9.2(@types/node@24.10.1)(typescript@5.8.3) tsconfig-paths: 4.2.0 transitivePeerDependencies: @@ -7070,14 +7074,14 @@ snapshots: - chokidar - typescript - '@angular-builders/custom-webpack@20.0.0(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)': + '@angular-builders/custom-webpack@20.0.0(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)': dependencies: '@angular-builders/common': 4.0.0(@types/node@24.10.1)(chokidar@4.0.3)(typescript@5.8.3) '@angular-devkit/architect': 0.2000.4(chokidar@4.0.3) - '@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) - '@angular/build': 20.3.10(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + '@angular/build': 20.3.13(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) lodash: 4.17.21 webpack-merge: 6.0.1 transitivePeerDependencies: @@ -7124,17 +7128,17 @@ snapshots: - webpack-cli - yaml - '@angular-builders/jest@20.0.0(2a2dc918d42153e5c2c5a3eb18182c36)': + '@angular-builders/jest@20.0.0(6f21bd249f8e5dee2ad8e6487b28db46)': dependencies: '@angular-builders/common': 4.0.0(@types/node@24.10.1)(chokidar@4.0.3)(typescript@5.8.3) '@angular-devkit/architect': 0.2000.4(chokidar@4.0.3) - '@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser-dynamic': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))) + '@angular-devkit/build-angular': 20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser-dynamic': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))) jest: 30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)) - jest-preset-angular: 14.6.0(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(canvas@3.0.0)(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3) + jest-preset-angular: 14.6.0(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(canvas@3.0.0)(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3) lodash: 4.17.21 transitivePeerDependencies: - '@babel/core' @@ -7166,14 +7170,21 @@ snapshots: transitivePeerDependencies: - chokidar - '@angular-devkit/build-angular@20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)': + '@angular-devkit/architect@0.2003.13(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + rxjs: 7.8.2 + transitivePeerDependencies: + - chokidar + + '@angular-devkit/build-angular@20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jest-environment-jsdom@30.2.0(canvas@3.0.0))(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jiti@1.21.7)(typescript@5.8.3)(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0))(yaml@2.7.0)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2000.4(chokidar@4.0.3) '@angular-devkit/build-webpack': 0.2000.4(chokidar@4.0.3)(webpack-dev-server@5.2.1(webpack@5.103.0))(webpack@5.99.8(esbuild@0.25.5)) '@angular-devkit/core': 20.0.4(chokidar@4.0.3) - '@angular/build': 20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular/build': 20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) '@babel/core': 7.27.1 '@babel/generator': 7.27.1 '@babel/helper-annotate-as-pure': 7.27.1 @@ -7184,7 +7195,7 @@ snapshots: '@babel/preset-env': 7.27.2(@babel/core@7.27.1) '@babel/runtime': 7.27.1 '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.99.8(esbuild@0.25.5)) + '@ngtools/webpack': 20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.99.8(esbuild@0.25.5)) '@vitejs/plugin-basic-ssl': 2.0.0(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0)) ansi-colors: 4.1.3 autoprefixer: 10.4.21(postcss@8.5.3) @@ -7226,9 +7237,9 @@ snapshots: webpack-merge: 6.0.1 webpack-subresource-integrity: 5.1.0(webpack@5.99.8(esbuild@0.25.5)) optionalDependencies: - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/localize': 20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/localize': 20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) esbuild: 0.25.5 jest: 30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)) jest-environment-jsdom: 30.2.0(canvas@3.0.0) @@ -7287,9 +7298,20 @@ snapshots: optionalDependencies: chokidar: 4.0.3 - '@angular-devkit/schematics@20.3.10(chokidar@4.0.3)': + '@angular-devkit/core@20.3.13(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) + jsonc-parser: 3.3.1 + picomatch: 4.0.3 + rxjs: 7.8.2 + source-map: 0.7.6 + optionalDependencies: + chokidar: 4.0.3 + + '@angular-devkit/schematics@20.3.13(chokidar@4.0.3)': + dependencies: + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) jsonc-parser: 3.3.1 magic-string: 0.30.17 ora: 8.2.0 @@ -7300,7 +7322,7 @@ snapshots: '@angular-eslint/builder@20.6.0(chokidar@4.0.3)(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@angular-devkit/architect': 0.2003.10(chokidar@4.0.3) - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) eslint: 9.39.1(jiti@1.21.7) typescript: 5.8.3 transitivePeerDependencies: @@ -7331,8 +7353,8 @@ snapshots: '@angular-eslint/schematics@20.6.0(@angular-eslint/template-parser@20.6.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(@typescript-eslint/types@8.48.1)(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(chokidar@4.0.3)(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) - '@angular-devkit/schematics': 20.3.10(chokidar@4.0.3) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + '@angular-devkit/schematics': 20.3.13(chokidar@4.0.3) '@angular-eslint/eslint-plugin': 20.6.0(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) '@angular-eslint/eslint-plugin-template': 20.6.0(@angular-eslint/template-parser@20.6.0(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(@typescript-eslint/types@8.48.1)(@typescript-eslint/utils@8.48.1(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3))(eslint@9.39.1(jiti@1.21.7))(typescript@5.8.3) ignore: 7.0.5 @@ -7360,12 +7382,12 @@ snapshots: eslint: 9.39.1(jiti@1.21.7) typescript: 5.8.3 - '@angular/build@20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)': + '@angular/build@20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)': dependencies: '@ampproject/remapping': 2.3.0 '@angular-devkit/architect': 0.2000.4(chokidar@4.0.3) - '@angular/compiler': 20.3.12 - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular/compiler': 20.3.15 + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) '@babel/core': 7.27.1 '@babel/helper-annotate-as-pure': 7.27.1 '@babel/helper-split-export-declaration': 7.24.7 @@ -7393,9 +7415,9 @@ snapshots: vite: 6.3.5(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.88.0)(terser@5.39.1)(yaml@2.7.0) watchpack: 2.4.2 optionalDependencies: - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/localize': 20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/localize': 20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) less: 4.3.0 lmdb: 3.3.0 postcss: 8.5.3 @@ -7412,19 +7434,19 @@ snapshots: - tsx - yaml - '@angular/build@20.3.10(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)': + '@angular/build@20.3.13(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(@types/node@24.10.1)(chokidar@4.0.3)(jiti@1.21.7)(less@4.3.0)(postcss@8.5.3)(terser@5.39.1)(tslib@2.8.1)(typescript@5.8.3)(yaml@2.7.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2003.10(chokidar@4.0.3) - '@angular/compiler': 20.3.12 - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular-devkit/architect': 0.2003.13(chokidar@4.0.3) + '@angular/compiler': 20.3.15 + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) '@babel/core': 7.28.3 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 '@inquirer/confirm': 5.1.14(@types/node@24.10.1) '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0)) beasties: 0.3.5 - browserslist: 4.28.0 + browserslist: 4.28.1 esbuild: 0.25.9 https-proxy-agent: 7.0.6 istanbul-lib-instrument: 6.0.3 @@ -7445,9 +7467,9 @@ snapshots: vite: 7.1.11(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.90.0)(terser@5.39.1)(yaml@2.7.0) watchpack: 2.4.4 optionalDependencies: - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/localize': 20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/localize': 20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) less: 4.3.0 lmdb: 3.4.2 postcss: 8.5.3 @@ -7464,23 +7486,23 @@ snapshots: - tsx - yaml - '@angular/cdk@20.2.13(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/cdk@20.2.13(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) parse5: 8.0.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/cli@20.3.10(@types/node@24.10.1)(chokidar@4.0.3)': + '@angular/cli@20.3.13(@types/node@24.10.1)(chokidar@4.0.3)': dependencies: - '@angular-devkit/architect': 0.2003.10(chokidar@4.0.3) - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) - '@angular-devkit/schematics': 20.3.10(chokidar@4.0.3) + '@angular-devkit/architect': 0.2003.13(chokidar@4.0.3) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + '@angular-devkit/schematics': 20.3.13(chokidar@4.0.3) '@inquirer/prompts': 7.8.2(@types/node@24.10.1) '@listr2/prompt-adapter-inquirer': 3.0.1(@inquirer/prompts@7.8.2(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.1) - '@modelcontextprotocol/sdk': 1.17.3 - '@schematics/angular': 20.3.10(chokidar@4.0.3) + '@modelcontextprotocol/sdk': 1.24.0(zod@4.1.13) + '@schematics/angular': 20.3.13(chokidar@4.0.3) '@yarnpkg/lockfile': 1.1.0 algoliasearch: 5.35.0 ini: 5.0.0 @@ -7491,21 +7513,22 @@ snapshots: resolve: 1.22.10 semver: 7.7.2 yargs: 18.0.0 - zod: 3.25.76 + zod: 4.1.13 transitivePeerDependencies: + - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3)': + '@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3)': dependencies: - '@angular/compiler': 20.3.12 + '@angular/compiler': 20.3.15 '@babel/core': 7.28.3 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 4.0.3 @@ -7519,30 +7542,30 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@20.3.12': + '@angular/compiler@20.3.15': dependencies: tslib: 2.8.1 - '@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)': + '@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 20.3.12 + '@angular/compiler': 20.3.15 zone.js: 0.15.1 - '@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12)': + '@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15)': dependencies: - '@angular/compiler': 20.3.12 - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular/compiler': 20.3.15 + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) '@babel/core': 7.28.3 '@types/babel__core': 7.20.5 tinyglobby: 0.2.15 @@ -7550,25 +7573,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))': + '@angular/platform-browser-dynamic@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/compiler': 20.3.12 - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': 20.3.15 + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) tslib: 2.8.1 - '@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))': + '@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - '@angular/router@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/router@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 @@ -7685,7 +7708,7 @@ snapshots: '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.1) @@ -7698,7 +7721,7 @@ snapshots: '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.2.0 semver: 6.3.1 @@ -7765,7 +7788,7 @@ snapshots: '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.27.1 '@babel/traverse': 7.28.5 transitivePeerDependencies: @@ -8201,7 +8224,7 @@ snapshots: '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.1)': dependencies: '@babel/core': 7.27.1 - '@babel/helper-annotate-as-pure': 7.27.1 + '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.1) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: @@ -9258,9 +9281,10 @@ snapshots: '@lmdb/lmdb-win32-x64@3.4.2': optional: true - '@modelcontextprotocol/sdk@1.17.3': + '@modelcontextprotocol/sdk@1.24.0(zod@4.1.13)': dependencies: - ajv: 6.12.6 + ajv: 8.17.1 + ajv-formats: 3.0.1(ajv@8.17.1) content-type: 1.0.5 cors: 2.8.5 cross-spawn: 7.0.6 @@ -9268,10 +9292,11 @@ snapshots: eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) + jose: 6.1.3 pkce-challenge: 5.0.0 raw-body: 3.0.1 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod: 4.1.13 + zod-to-json-schema: 3.25.0(zod@4.1.13) transitivePeerDependencies: - supports-color @@ -9372,35 +9397,35 @@ snapshots: '@tybys/wasm-util': 0.10.1 optional: true - '@ng-bootstrap/ng-bootstrap@19.0.1(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@19.0.1(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/forms': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) - '@angular/localize': 20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/localize': 20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@20.7.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))': + '@ng-select/ng-select@20.7.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))': dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/forms': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) tslib: 2.8.1 - '@ngneat/dirty-check-forms@3.0.3(8ff1ffec9c0eb3e42a8b58cc79f67aaa)': + '@ngneat/dirty-check-forms@3.0.3(218c118fbd72b8c5b4f5fd1fa1211ac5)': dependencies: - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/forms': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) - '@angular/router': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/router': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) lodash-es: 4.17.21 rxjs: 7.8.2 tslib: 2.8.1 - '@ngtools/webpack@20.0.4(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.99.8(esbuild@0.25.5))': + '@ngtools/webpack@20.0.4(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(typescript@5.8.3)(webpack@5.99.8(esbuild@0.25.5))': dependencies: - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) typescript: 5.8.3 webpack: 5.99.8(esbuild@0.25.5) @@ -9428,7 +9453,7 @@ snapshots: '@npmcli/fs@4.0.0': dependencies: - semver: 7.7.2 + semver: 7.7.3 '@npmcli/git@6.0.3': dependencies: @@ -9438,7 +9463,7 @@ snapshots: npm-pick-manifest: 10.0.0 proc-log: 5.0.0 promise-retry: 2.0.1 - semver: 7.7.2 + semver: 7.7.3 which: 5.0.0 '@npmcli/installed-package-contents@3.0.0': @@ -9455,7 +9480,7 @@ snapshots: hosted-git-info: 8.1.0 json-parse-even-better-errors: 4.0.0 proc-log: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-license: 3.0.4 '@npmcli/promise-spawn@8.0.3': @@ -9731,10 +9756,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.52.3': optional: true - '@schematics/angular@20.3.10(chokidar@4.0.3)': + '@schematics/angular@20.3.13(chokidar@4.0.3)': dependencies: - '@angular-devkit/core': 20.3.10(chokidar@4.0.3) - '@angular-devkit/schematics': 20.3.10(chokidar@4.0.3) + '@angular-devkit/core': 20.3.13(chokidar@4.0.3) + '@angular-devkit/schematics': 20.3.13(chokidar@4.0.3) jsonc-parser: 3.3.1 transitivePeerDependencies: - chokidar @@ -10446,8 +10471,6 @@ snapshots: base64-js@1.5.1: optional: true - baseline-browser-mapping@2.8.29: {} - baseline-browser-mapping@2.9.4: {} batch@0.6.1: {} @@ -10460,7 +10483,7 @@ snapshots: domhandler: 5.0.3 htmlparser2: 10.0.0 picocolors: 1.1.1 - postcss: 8.5.3 + postcss: 8.5.6 postcss-media-query-parser: 0.2.3 beasties@0.3.5: @@ -10542,14 +10565,6 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.0: - dependencies: - baseline-browser-mapping: 2.8.29 - caniuse-lite: 1.0.30001755 - electron-to-chromium: 1.5.255 - node-releases: 2.0.27 - update-browserslist-db: 1.1.4(browserslist@4.28.0) - browserslist@4.28.1: dependencies: baseline-browser-mapping: 2.9.4 @@ -10611,8 +10626,6 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001755: {} - caniuse-lite@1.0.30001759: {} canvas@3.0.0: @@ -10807,7 +10820,7 @@ snapshots: postcss-modules-scope: 3.2.1(postcss@8.5.3) postcss-modules-values: 4.0.0(postcss@8.5.3) postcss-value-parser: 4.2.0 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: webpack: 5.99.8(esbuild@0.25.5) @@ -10955,8 +10968,6 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.255: {} - electron-to-chromium@1.5.266: {} emittery@0.13.1: {} @@ -11354,10 +11365,6 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.2): - optionalDependencies: - picomatch: 4.0.2 - fdir@6.5.0(picomatch@4.0.3): optionalDependencies: picomatch: 4.0.3 @@ -11810,11 +11817,11 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.28.5 '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 - semver: 7.7.2 + semver: 7.7.3 transitivePeerDependencies: - supports-color @@ -12102,11 +12109,11 @@ snapshots: optionalDependencies: jest-resolve: 30.2.0 - jest-preset-angular@14.6.0(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(canvas@3.0.0)(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3): + jest-preset-angular@14.6.0(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser-dynamic@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))))(@babel/core@7.28.5)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.5))(canvas@3.0.0)(jest@30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@5.8.3): dependencies: - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser-dynamic': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser-dynamic': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))) bs-logger: 0.2.6 esbuild-wasm: 0.25.10 jest: 30.2.0(@types/node@24.10.1)(ts-node@10.9.2(@types/node@24.10.1)(typescript@5.8.3)) @@ -12128,12 +12135,12 @@ snapshots: - supports-color - utf-8-validate - jest-preset-angular@15.0.3(138e950b6256ba9944139a8c5aad3bdf): + jest-preset-angular@15.0.3(e164a15bea80c603eb581d5724a352ec): dependencies: - '@angular/compiler-cli': 20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)) - '@angular/platform-browser-dynamic': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.12)(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))) + '@angular/compiler-cli': 20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/platform-browser-dynamic': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@20.3.15)(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))) '@jest/environment-jsdom-abstract': 30.2.0(canvas@3.0.0)(jsdom@26.1.0(canvas@3.0.0)) bs-logger: 0.2.6 esbuild-wasm: 0.27.0 @@ -12333,6 +12340,8 @@ snapshots: jiti@1.21.7: {} + jose@6.1.3: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -12801,46 +12810,46 @@ snapshots: pdfjs-dist: 4.8.69 tslib: 2.8.1 - ngx-bootstrap-icons@1.9.3(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)): + ngx-bootstrap-icons@1.9.3(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - ngx-color@10.1.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)): + ngx-color@10.1.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) '@ctrl/tinycolor': 4.2.0 material-colors: 1.2.6 tslib: 2.8.1 - ngx-cookie-service@20.1.1(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)): + ngx-cookie-service@20.1.1(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - ngx-device-detector@10.1.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)): + ngx-device-detector@10.1.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - ngx-ui-tour-core@15.0.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2): + ngx-ui-tour-core@15.0.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/router': 20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/router': 20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - ngx-ui-tour-ng-bootstrap@17.0.1(1990d937b48b356c3a7b81a3382c7bae): + ngx-ui-tour-ng-bootstrap@17.0.1(43e0b240967005bbda3e3b71e72070f1): dependencies: - '@angular/common': 20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1) - '@ng-bootstrap/ng-bootstrap': 19.0.1(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.12(@angular/compiler-cli@20.3.12(@angular/compiler@20.3.12)(typescript@5.8.3))(@angular/compiler@20.3.12))(@popperjs/core@2.11.8)(rxjs@7.8.2) - ngx-ui-tour-core: 15.0.0(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.12(@angular/common@20.3.14(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.12(@angular/compiler@20.3.12)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2) + '@angular/common': 20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1) + '@ng-bootstrap/ng-bootstrap': 19.0.1(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/forms@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(@angular/localize@20.3.15(@angular/compiler-cli@20.3.15(@angular/compiler@20.3.15)(typescript@5.8.3))(@angular/compiler@20.3.15))(@popperjs/core@2.11.8)(rxjs@7.8.2) + ngx-ui-tour-core: 15.0.0(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/router@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@20.3.15(@angular/common@20.3.15(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@20.3.15(@angular/compiler@20.3.15)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2))(rxjs@7.8.2) tslib: 2.8.1 transitivePeerDependencies: - '@angular/router' @@ -12872,7 +12881,7 @@ snapshots: make-fetch-happen: 14.0.3 nopt: 8.1.0 proc-log: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 tar: 7.5.2 tinyglobby: 0.2.15 which: 5.0.0 @@ -12897,7 +12906,7 @@ snapshots: npm-install-checks@7.1.2: dependencies: - semver: 7.7.2 + semver: 7.7.3 npm-normalize-package-bin@4.0.0: {} @@ -12905,14 +12914,14 @@ snapshots: dependencies: hosted-git-info: 8.1.0 proc-log: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-name: 6.0.2 npm-package-arg@13.0.0: dependencies: hosted-git-info: 9.0.2 proc-log: 5.0.0 - semver: 7.7.2 + semver: 7.7.3 validate-npm-package-name: 6.0.2 npm-packlist@10.0.3: @@ -12925,7 +12934,7 @@ snapshots: npm-install-checks: 7.1.2 npm-normalize-package-bin: 4.0.0 npm-package-arg: 12.0.2 - semver: 7.7.2 + semver: 7.7.3 npm-registry-fetch@18.0.2: dependencies: @@ -13165,7 +13174,7 @@ snapshots: cosmiconfig: 9.0.0(typescript@5.8.3) jiti: 1.21.7 postcss: 8.5.3 - semver: 7.7.2 + semver: 7.7.3 optionalDependencies: webpack: 5.99.8(esbuild@0.25.5) transitivePeerDependencies: @@ -13962,8 +13971,8 @@ snapshots: tinyglobby@0.2.13: dependencies: - fdir: 6.5.0(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 tinyglobby@0.2.14: dependencies: @@ -14189,12 +14198,6 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - update-browserslist-db@1.1.4(browserslist@4.28.0): - dependencies: - browserslist: 4.28.0 - escalade: 3.2.0 - picocolors: 1.1.1 - update-browserslist-db@1.2.2(browserslist@4.28.1): dependencies: browserslist: 4.28.1 @@ -14241,12 +14244,12 @@ snapshots: vite@6.3.5(@types/node@24.10.1)(jiti@1.21.7)(less@4.3.0)(sass@1.88.0)(terser@5.39.1)(yaml@2.7.0): dependencies: - esbuild: 0.25.5 - fdir: 6.5.0(picomatch@4.0.2) - picomatch: 4.0.2 - postcss: 8.5.3 - rollup: 4.40.2 - tinyglobby: 0.2.13 + esbuild: 0.25.9 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.52.3 + tinyglobby: 0.2.15 optionalDependencies: '@types/node': 24.10.1 fsevents: 2.3.3 @@ -14605,10 +14608,12 @@ snapshots: yoctocolors-cjs@2.1.3: {} - zod-to-json-schema@3.24.6(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@4.1.13): dependencies: - zod: 3.25.76 + zod: 4.1.13 zod@3.25.76: {} + zod@4.1.13: {} + zone.js@0.15.1: {} From 26975868a09656a78e76ad9dad7d60f9ea280017 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 20:17:39 +0000 Subject: [PATCH 4/8] Auto translate strings --- src-ui/messages.xlf | 70 ++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 48c54782d..c48eba6e9 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -5,14 +5,14 @@ Close - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/alert/alert.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/alert/alert.ts 50 Slide of - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/carousel/carousel.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/carousel/carousel.ts 131,135 Currently selected slide number read by screen reader @@ -20,212 +20,212 @@ Previous - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/carousel/carousel.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/carousel/carousel.ts 157,159 Next - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/carousel/carousel.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/carousel/carousel.ts 198 Previous month - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/datepicker/datepicker-navigation.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/datepicker/datepicker-navigation.ts 83,85 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/datepicker/datepicker-navigation.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/datepicker/datepicker-navigation.ts 112 Next month - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/datepicker/datepicker-navigation.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/datepicker/datepicker-navigation.ts 112 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/datepicker/datepicker-navigation.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/datepicker/datepicker-navigation.ts 112 HH - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Close - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Select month - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 «« - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 « - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 MM - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 » - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Select year - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 »» - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 First - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Increment hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Previous - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Decrement hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Next - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Increment minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Last - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Decrement minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 SS - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Increment seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 Decrement seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/ngb-config.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/ngb-config.ts 13 @@ -233,7 +233,7 @@ - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.14_@angular+core@20.3.12_@angula_f6978d5a33be250eb7b5e8e65faf7a7d/node_modules/src/progressbar/progressbar.ts + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@19.0.1_@angular+common@20.3.15_@angular+core@20.3.15_@angula_40533c760dbaadbd90323f0d78d15fb8/node_modules/src/progressbar/progressbar.ts 41,42 From 128c3539d50fa66d4ce152a84fa7561b299c4714 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Tue, 9 Dec 2025 16:12:40 -0800 Subject: [PATCH 5/8] Chore: fix set_permissions_for_object type (#11564) --- src/documents/permissions.py | 17 +++++++++-------- src/documents/tests/test_api_search.py | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/documents/permissions.py b/src/documents/permissions.py index 802cb8798..ac6d3f9ca 100644 --- a/src/documents/permissions.py +++ b/src/documents/permissions.py @@ -61,21 +61,22 @@ def get_groups_with_only_permission(obj, codename): return Group.objects.filter(id__in=group_object_perm_group_ids).distinct() -def set_permissions_for_object(permissions: list[str], object, *, merge: bool = False): +def set_permissions_for_object(permissions: dict, object, *, merge: bool = False): """ - Set permissions for an object. The permissions are given as a list of strings - in the format "action_modelname", e.g. "view_document". + Set permissions for an object. The permissions are given as a mapping of actions + to a dict of user / group id lists, e.g. + {"view": {"users": [1], "groups": [2]}, "change": {"users": [], "groups": []}}. If merge is True, the permissions are merged with the existing permissions and no users or groups are removed. If False, the permissions are set to exactly the given list of users and groups. """ - for action in permissions: + for action, entry in permissions.items(): permission = f"{action}_{object.__class__.__name__.lower()}" - if "users" in permissions[action]: + if "users" in entry: # users - users_to_add = User.objects.filter(id__in=permissions[action]["users"]) + users_to_add = User.objects.filter(id__in=entry["users"]) users_to_remove = ( get_users_with_perms( object, @@ -100,9 +101,9 @@ def set_permissions_for_object(permissions: list[str], object, *, merge: bool = user, object, ) - if "groups" in permissions[action]: + if "groups" in entry: # groups - groups_to_add = Group.objects.filter(id__in=permissions[action]["groups"]) + groups_to_add = Group.objects.filter(id__in=entry["groups"]) groups_to_remove = ( get_groups_with_only_permission( object, diff --git a/src/documents/tests/test_api_search.py b/src/documents/tests/test_api_search.py index 5a2fc9b52..191381721 100644 --- a/src/documents/tests/test_api_search.py +++ b/src/documents/tests/test_api_search.py @@ -1289,7 +1289,7 @@ class TestDocumentSearchApi(DirectoriesMixin, APITestCase): content_type__app_label="admin", ), ) - set_permissions([4, 5], set_permissions=[], owner=user2, merge=False) + set_permissions([4, 5], set_permissions={}, owner=user2, merge=False) with index.open_index_writer() as writer: index.update_document(writer, d1) From 317f239d09d2ddaaa633211c09db5f66a11db048 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Wed, 10 Dec 2025 08:38:28 -0800 Subject: [PATCH 6/8] Fix: pass additional arguments to TagSerializer for permissions (#11576) --- src/documents/serialisers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/documents/serialisers.py b/src/documents/serialisers.py index f04bb70da..3cb2b00af 100644 --- a/src/documents/serialisers.py +++ b/src/documents/serialisers.py @@ -585,6 +585,9 @@ class TagSerializer(MatchingModelSerializer, OwnedObjectSerializer): .select_related("owner") .annotate(document_count=Count("documents", filter=filter_q)), many=True, + user=self.user, + full_perms=self.full_perms, + all_fields=self.all_fields, context=self.context, ) return serializer.data From c845cf0a194287c33465b35cffba07c628270765 Mon Sep 17 00:00:00 2001 From: GitHub Actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:40:14 +0000 Subject: [PATCH 7/8] Auto translate strings --- src/locale/en_US/LC_MESSAGES/django.po | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index bc2985431..8e745a2e9 100644 --- a/src/locale/en_US/LC_MESSAGES/django.po +++ b/src/locale/en_US/LC_MESSAGES/django.po @@ -2,7 +2,7 @@ msgid "" msgstr "" "Project-Id-Version: paperless-ngx\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-11-14 16:09+0000\n" +"POT-Creation-Date: 2025-12-10 16:39+0000\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -1224,35 +1224,35 @@ msgstr "" msgid "Invalid regular expression: %(error)s" msgstr "" -#: documents/serialisers.py:619 +#: documents/serialisers.py:622 msgid "Invalid color." msgstr "" -#: documents/serialisers.py:1805 +#: documents/serialisers.py:1808 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:1849 +#: documents/serialisers.py:1852 #, python-format msgid "Custom field id must be an integer: %(id)s" msgstr "" -#: documents/serialisers.py:1856 +#: documents/serialisers.py:1859 #, python-format msgid "Custom field with id %(id)s does not exist" msgstr "" -#: documents/serialisers.py:1873 documents/serialisers.py:1883 +#: documents/serialisers.py:1876 documents/serialisers.py:1886 msgid "" "Custom fields must be a list of integers or an object mapping ids to values." msgstr "" -#: documents/serialisers.py:1878 +#: documents/serialisers.py:1881 msgid "Some custom fields don't exist or were specified twice." msgstr "" -#: documents/serialisers.py:1993 +#: documents/serialisers.py:1996 msgid "Invalid variable detected." msgstr "" From 66d363bdc5ceaf6780683baf0ab8a42c6ea5e8af Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Thu, 11 Dec 2025 12:13:10 -0800 Subject: [PATCH 8/8] Chore: refactor workflows code (#11563) --- src/documents/signals/handlers.py | 734 ++------------------------ src/documents/tasks.py | 11 +- src/documents/tests/test_workflows.py | 22 +- src/documents/workflows/__init__.py | 0 src/documents/workflows/actions.py | 261 +++++++++ src/documents/workflows/mutations.py | 357 +++++++++++++ src/documents/workflows/utils.py | 116 ++++ src/documents/workflows/webhooks.py | 96 ++++ 8 files changed, 890 insertions(+), 707 deletions(-) create mode 100644 src/documents/workflows/__init__.py create mode 100644 src/documents/workflows/actions.py create mode 100644 src/documents/workflows/mutations.py create mode 100644 src/documents/workflows/utils.py create mode 100644 src/documents/workflows/webhooks.py diff --git a/src/documents/signals/handlers.py b/src/documents/signals/handlers.py index e410b54e2..6cafbc1f8 100644 --- a/src/documents/signals/handlers.py +++ b/src/documents/signals/handlers.py @@ -1,14 +1,10 @@ from __future__ import annotations -import ipaddress import logging import shutil -import socket from pathlib import Path from typing import TYPE_CHECKING -from urllib.parse import urlparse -import httpx from celery import shared_task from celery import states from celery.signals import before_task_publish @@ -27,20 +23,15 @@ from django.db.models import Q from django.dispatch import receiver from django.utils import timezone from filelock import FileLock -from guardian.shortcuts import remove_perm from documents import matching from documents.caching import clear_document_caches from documents.file_handling import create_source_path_directory from documents.file_handling import delete_empty_directories from documents.file_handling import generate_unique_filename -from documents.mail import EmailAttachment -from documents.mail import send_email -from documents.models import Correspondent from documents.models import CustomField from documents.models import CustomFieldInstance from documents.models import Document -from documents.models import DocumentType from documents.models import MatchingModel from documents.models import PaperlessTask from documents.models import SavedView @@ -51,8 +42,14 @@ from documents.models import WorkflowAction from documents.models import WorkflowRun from documents.models import WorkflowTrigger from documents.permissions import get_objects_for_user_owner_aware -from documents.permissions import set_permissions_for_object -from documents.templating.workflows import parse_w_workflow_placeholders +from documents.workflows.actions import build_workflow_action_context +from documents.workflows.actions import execute_email_action +from documents.workflows.actions import execute_webhook_action +from documents.workflows.mutations import apply_assignment_to_document +from documents.workflows.mutations import apply_assignment_to_overrides +from documents.workflows.mutations import apply_removal_to_document +from documents.workflows.mutations import apply_removal_to_overrides +from documents.workflows.utils import get_workflows_for_trigger if TYPE_CHECKING: from documents.classifier import DocumentClassifier @@ -673,92 +670,6 @@ def run_workflows_updated(sender, document: Document, logging_group=None, **kwar ) -def _is_public_ip(ip: str) -> bool: - try: - obj = ipaddress.ip_address(ip) - return not ( - obj.is_private - or obj.is_loopback - or obj.is_link_local - or obj.is_multicast - or obj.is_unspecified - ) - except ValueError: # pragma: no cover - return False - - -def _resolve_first_ip(host: str) -> str | None: - try: - info = socket.getaddrinfo(host, None) - return info[0][4][0] if info else None - except Exception: # pragma: no cover - return None - - -@shared_task( - retry_backoff=True, - autoretry_for=(httpx.HTTPStatusError,), - max_retries=3, - throws=(httpx.HTTPError,), -) -def send_webhook( - url: str, - data: str | dict, - headers: dict, - files: dict, - *, - as_json: bool = False, -): - p = urlparse(url) - if p.scheme.lower() not in settings.WEBHOOKS_ALLOWED_SCHEMES or not p.hostname: - logger.warning("Webhook blocked: invalid scheme/hostname") - raise ValueError("Invalid URL scheme or hostname.") - - port = p.port or (443 if p.scheme == "https" else 80) - if ( - len(settings.WEBHOOKS_ALLOWED_PORTS) > 0 - and port not in settings.WEBHOOKS_ALLOWED_PORTS - ): - logger.warning("Webhook blocked: port not permitted") - raise ValueError("Destination port not permitted.") - - ip = _resolve_first_ip(p.hostname) - if not ip or ( - not _is_public_ip(ip) and not settings.WEBHOOKS_ALLOW_INTERNAL_REQUESTS - ): - logger.warning("Webhook blocked: destination not allowed") - raise ValueError("Destination host is not allowed.") - - try: - post_args = { - "url": url, - "headers": { - k: v for k, v in (headers or {}).items() if k.lower() != "host" - }, - "files": files or None, - "timeout": 5.0, - "follow_redirects": False, - } - if as_json: - post_args["json"] = data - elif isinstance(data, dict): - post_args["data"] = data - else: - post_args["content"] = data - - httpx.post( - **post_args, - ).raise_for_status() - logger.info( - f"Webhook sent to {url}", - ) - except Exception as e: - logger.error( - f"Failed attempt sending webhook to {url}: {e}", - ) - raise e - - def run_workflows( trigger_type: WorkflowTrigger.WorkflowTriggerType, document: Document | ConsumableDocument, @@ -767,572 +678,16 @@ def run_workflows( overrides: DocumentMetadataOverrides | None = None, original_file: Path | None = None, ) -> tuple[DocumentMetadataOverrides, str] | None: - """Run workflows which match a Document (or ConsumableDocument) for a specific trigger type or a single workflow if given. - - Assignment or removal actions are either applied directly to the document or an overrides object. If an overrides - object is provided, the function returns the object with the applied changes or None if no actions were applied and a string - of messages for each action. If no overrides object is provided, the changes are applied directly to the document and the - function returns None. """ + Execute workflows matching a document for the given trigger. When `overrides` is provided + (consumption flow), actions mutate that object and the function returns `(overrides, messages)`. + Otherwise actions mutate the actual document and return nothing. - def assignment_action(): - if action.assign_tags.exists(): - tag_ids_to_add: set[int] = set() - for tag in action.assign_tags.all(): - tag_ids_to_add.add(tag.pk) - tag_ids_to_add.update(int(pk) for pk in tag.get_ancestors_pks()) + Attachments for email/webhook actions use `original_file` when given, otherwise fall back to + `document.source_path` (Document) or `document.original_file` (ConsumableDocument). - if not use_overrides: - doc_tag_ids[:] = list(set(doc_tag_ids) | tag_ids_to_add) - else: - if overrides.tag_ids is None: - overrides.tag_ids = [] - overrides.tag_ids = list(set(overrides.tag_ids) | tag_ids_to_add) - - if action.assign_correspondent: - if not use_overrides: - document.correspondent = action.assign_correspondent - else: - overrides.correspondent_id = action.assign_correspondent.pk - - if action.assign_document_type: - if not use_overrides: - document.document_type = action.assign_document_type - else: - overrides.document_type_id = action.assign_document_type.pk - - if action.assign_storage_path: - if not use_overrides: - document.storage_path = action.assign_storage_path - else: - overrides.storage_path_id = action.assign_storage_path.pk - - if action.assign_owner: - if not use_overrides: - document.owner = action.assign_owner - else: - overrides.owner_id = action.assign_owner.pk - - if action.assign_title: - if not use_overrides: - try: - document.title = parse_w_workflow_placeholders( - action.assign_title, - document.correspondent.name if document.correspondent else "", - document.document_type.name if document.document_type else "", - document.owner.username if document.owner else "", - timezone.localtime(document.added), - document.original_filename or "", - document.filename or "", - document.created, - ) - except Exception: - logger.exception( - f"Error occurred parsing title assignment '{action.assign_title}', falling back to original", - extra={"group": logging_group}, - ) - else: - overrides.title = action.assign_title - - if any( - [ - action.assign_view_users.exists(), - action.assign_view_groups.exists(), - action.assign_change_users.exists(), - action.assign_change_groups.exists(), - ], - ): - permissions = { - "view": { - "users": action.assign_view_users.values_list("id", flat=True), - "groups": action.assign_view_groups.values_list("id", flat=True), - }, - "change": { - "users": action.assign_change_users.values_list("id", flat=True), - "groups": action.assign_change_groups.values_list("id", flat=True), - }, - } - if not use_overrides: - set_permissions_for_object( - permissions=permissions, - object=document, - merge=True, - ) - else: - overrides.view_users = list( - set( - (overrides.view_users or []) - + list(permissions["view"]["users"]), - ), - ) - overrides.view_groups = list( - set( - (overrides.view_groups or []) - + list(permissions["view"]["groups"]), - ), - ) - overrides.change_users = list( - set( - (overrides.change_users or []) - + list(permissions["change"]["users"]), - ), - ) - overrides.change_groups = list( - set( - (overrides.change_groups or []) - + list(permissions["change"]["groups"]), - ), - ) - - if action.assign_custom_fields.exists(): - if not use_overrides: - for field in action.assign_custom_fields.all(): - value_field_name = CustomFieldInstance.get_value_field_name( - data_type=field.data_type, - ) - args = { - value_field_name: action.assign_custom_fields_values.get( - str(field.pk), - None, - ), - } - # for some reason update_or_create doesn't work here - instance = CustomFieldInstance.objects.filter( - field=field, - document=document, - ).first() - if instance and args[value_field_name] is not None: - setattr(instance, value_field_name, args[value_field_name]) - instance.save() - elif not instance: - CustomFieldInstance.objects.create( - **args, - field=field, - document=document, - ) - else: - if overrides.custom_fields is None: - overrides.custom_fields = {} - overrides.custom_fields.update( - { - field.pk: action.assign_custom_fields_values.get( - str(field.pk), - None, - ) - for field in action.assign_custom_fields.all() - }, - ) - - def removal_action(): - if action.remove_all_tags: - if not use_overrides: - doc_tag_ids.clear() - else: - overrides.tag_ids = None - else: - tag_ids_to_remove: set[int] = set() - for tag in action.remove_tags.all(): - tag_ids_to_remove.add(tag.pk) - tag_ids_to_remove.update(int(pk) for pk in tag.get_descendants_pks()) - - if not use_overrides: - doc_tag_ids[:] = [t for t in doc_tag_ids if t not in tag_ids_to_remove] - elif overrides.tag_ids: - overrides.tag_ids = [ - t for t in overrides.tag_ids if t not in tag_ids_to_remove - ] - - if not use_overrides and ( - action.remove_all_correspondents - or ( - document.correspondent - and action.remove_correspondents.filter( - pk=document.correspondent.pk, - ).exists() - ) - ): - document.correspondent = None - elif use_overrides and ( - action.remove_all_correspondents - or ( - overrides.correspondent_id - and action.remove_correspondents.filter( - pk=overrides.correspondent_id, - ).exists() - ) - ): - overrides.correspondent_id = None - - if not use_overrides and ( - action.remove_all_document_types - or ( - document.document_type - and action.remove_document_types.filter( - pk=document.document_type.pk, - ).exists() - ) - ): - document.document_type = None - elif use_overrides and ( - action.remove_all_document_types - or ( - overrides.document_type_id - and action.remove_document_types.filter( - pk=overrides.document_type_id, - ).exists() - ) - ): - overrides.document_type_id = None - - if not use_overrides and ( - action.remove_all_storage_paths - or ( - document.storage_path - and action.remove_storage_paths.filter( - pk=document.storage_path.pk, - ).exists() - ) - ): - document.storage_path = None - elif use_overrides and ( - action.remove_all_storage_paths - or ( - overrides.storage_path_id - and action.remove_storage_paths.filter( - pk=overrides.storage_path_id, - ).exists() - ) - ): - overrides.storage_path_id = None - - if not use_overrides and ( - action.remove_all_owners - or ( - document.owner - and action.remove_owners.filter(pk=document.owner.pk).exists() - ) - ): - document.owner = None - elif use_overrides and ( - action.remove_all_owners - or ( - overrides.owner_id - and action.remove_owners.filter(pk=overrides.owner_id).exists() - ) - ): - overrides.owner_id = None - - if action.remove_all_permissions: - if not use_overrides: - permissions = { - "view": {"users": [], "groups": []}, - "change": {"users": [], "groups": []}, - } - set_permissions_for_object( - permissions=permissions, - object=document, - merge=False, - ) - else: - overrides.view_users = None - overrides.view_groups = None - overrides.change_users = None - overrides.change_groups = None - elif any( - [ - action.remove_view_users.exists(), - action.remove_view_groups.exists(), - action.remove_change_users.exists(), - action.remove_change_groups.exists(), - ], - ): - if not use_overrides: - for user in action.remove_view_users.all(): - remove_perm("view_document", user, document) - for user in action.remove_change_users.all(): - remove_perm("change_document", user, document) - for group in action.remove_view_groups.all(): - remove_perm("view_document", group, document) - for group in action.remove_change_groups.all(): - remove_perm("change_document", group, document) - else: - if overrides.view_users: - for user in action.remove_view_users.filter( - pk__in=overrides.view_users, - ): - overrides.view_users.remove(user.pk) - if overrides.change_users: - for user in action.remove_change_users.filter( - pk__in=overrides.change_users, - ): - overrides.change_users.remove(user.pk) - if overrides.view_groups: - for group in action.remove_view_groups.filter( - pk__in=overrides.view_groups, - ): - overrides.view_groups.remove(group.pk) - if overrides.change_groups: - for group in action.remove_change_groups.filter( - pk__in=overrides.change_groups, - ): - overrides.change_groups.remove(group.pk) - - if action.remove_all_custom_fields: - if not use_overrides: - CustomFieldInstance.objects.filter(document=document).hard_delete() - else: - overrides.custom_fields = None - elif action.remove_custom_fields.exists(): - if not use_overrides: - CustomFieldInstance.objects.filter( - field__in=action.remove_custom_fields.all(), - document=document, - ).hard_delete() - elif overrides.custom_fields: - for field in action.remove_custom_fields.filter( - pk__in=overrides.custom_fields.keys(), - ): - overrides.custom_fields.pop(field.pk, None) - - def email_action(): - if not settings.EMAIL_ENABLED: - logger.error( - "Email backend has not been configured, cannot send email notifications", - extra={"group": logging_group}, - ) - return - - if not use_overrides: - title = document.title - doc_url = ( - f"{settings.PAPERLESS_URL}{settings.BASE_URL}documents/{document.pk}/" - ) - correspondent = ( - document.correspondent.name if document.correspondent else "" - ) - document_type = ( - document.document_type.name if document.document_type else "" - ) - owner_username = document.owner.username if document.owner else "" - filename = document.original_filename or "" - current_filename = document.filename or "" - added = timezone.localtime(document.added) - created = document.created - else: - title = overrides.title if overrides.title else str(document.original_file) - doc_url = "" - correspondent = ( - Correspondent.objects.filter(pk=overrides.correspondent_id).first() - if overrides.correspondent_id - else "" - ) - document_type = ( - DocumentType.objects.filter(pk=overrides.document_type_id).first().name - if overrides.document_type_id - else "" - ) - owner_username = ( - User.objects.filter(pk=overrides.owner_id).first().username - if overrides.owner_id - else "" - ) - filename = document.original_file if document.original_file else "" - current_filename = filename - added = timezone.localtime(timezone.now()) - created = overrides.created - - subject = ( - parse_w_workflow_placeholders( - action.email.subject, - correspondent, - document_type, - owner_username, - added, - filename, - current_filename, - created, - title, - doc_url, - ) - if action.email.subject - else "" - ) - body = ( - parse_w_workflow_placeholders( - action.email.body, - correspondent, - document_type, - owner_username, - added, - filename, - current_filename, - created, - title, - doc_url, - ) - if action.email.body - else "" - ) - try: - attachments: list[EmailAttachment] = [] - if action.email.include_document: - attachment: EmailAttachment | None = None - if trigger_type in [ - WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, - WorkflowTrigger.WorkflowTriggerType.SCHEDULED, - ] and isinstance(document, Document): - friendly_name = ( - Path(current_filename).name - if current_filename - else document.source_path.name - ) - attachment = EmailAttachment( - path=document.source_path, - mime_type=document.mime_type, - friendly_name=friendly_name, - ) - elif original_file: - friendly_name = ( - Path(current_filename).name - if current_filename - else original_file.name - ) - attachment = EmailAttachment( - path=original_file, - mime_type=document.mime_type, - friendly_name=friendly_name, - ) - if attachment: - attachments = [attachment] - n_messages = send_email( - subject=subject, - body=body, - to=action.email.to.split(","), - attachments=attachments, - ) - logger.debug( - f"Sent {n_messages} notification email(s) to {action.email.to}", - extra={"group": logging_group}, - ) - except Exception as e: - logger.exception( - f"Error occurred sending notification email: {e}", - extra={"group": logging_group}, - ) - - def webhook_action(): - if not use_overrides: - title = document.title - doc_url = ( - f"{settings.PAPERLESS_URL}{settings.BASE_URL}documents/{document.pk}/" - ) - correspondent = ( - document.correspondent.name if document.correspondent else "" - ) - document_type = ( - document.document_type.name if document.document_type else "" - ) - owner_username = document.owner.username if document.owner else "" - filename = document.original_filename or "" - current_filename = document.filename or "" - added = timezone.localtime(document.added) - created = document.created - else: - title = overrides.title if overrides.title else str(document.original_file) - doc_url = "" - correspondent = ( - Correspondent.objects.filter(pk=overrides.correspondent_id).first() - if overrides.correspondent_id - else "" - ) - document_type = ( - DocumentType.objects.filter(pk=overrides.document_type_id).first().name - if overrides.document_type_id - else "" - ) - owner_username = ( - User.objects.filter(pk=overrides.owner_id).first().username - if overrides.owner_id - else "" - ) - filename = document.original_file if document.original_file else "" - current_filename = filename - added = timezone.localtime(timezone.now()) - created = overrides.created - - try: - data = {} - if action.webhook.use_params: - if action.webhook.params: - try: - for key, value in action.webhook.params.items(): - data[key] = parse_w_workflow_placeholders( - value, - correspondent, - document_type, - owner_username, - added, - filename, - current_filename, - created, - title, - doc_url, - ) - except Exception as e: - logger.error( - f"Error occurred parsing webhook params: {e}", - extra={"group": logging_group}, - ) - elif action.webhook.body: - data = parse_w_workflow_placeholders( - action.webhook.body, - correspondent, - document_type, - owner_username, - added, - filename, - current_filename, - created, - title, - doc_url, - ) - headers = {} - if action.webhook.headers: - try: - headers = { - str(k): str(v) for k, v in action.webhook.headers.items() - } - except Exception as e: - logger.error( - f"Error occurred parsing webhook headers: {e}", - extra={"group": logging_group}, - ) - files = None - if action.webhook.include_document: - with original_file.open("rb") as f: - files = { - "file": ( - filename, - f.read(), - document.mime_type, - ), - } - send_webhook.delay( - url=action.webhook.url, - data=data, - headers=headers, - files=files, - as_json=action.webhook.as_json, - ) - logger.debug( - f"Webhook to {action.webhook.url} queued", - extra={"group": logging_group}, - ) - except Exception as e: - logger.exception( - f"Error occurred sending webhook: {e}", - extra={"group": logging_group}, - ) + Passing `workflow_to_run` skips the workflow query (currently only used by scheduled runs). + """ use_overrides = overrides is not None if original_file is None: @@ -1341,30 +696,7 @@ def run_workflows( ) messages = [] - workflows = ( - ( - Workflow.objects.filter(enabled=True, triggers__type=trigger_type) - .prefetch_related( - "actions", - "actions__assign_view_users", - "actions__assign_view_groups", - "actions__assign_change_users", - "actions__assign_change_groups", - "actions__assign_custom_fields", - "actions__remove_tags", - "actions__remove_correspondents", - "actions__remove_document_types", - "actions__remove_storage_paths", - "actions__remove_custom_fields", - "actions__remove_owners", - "triggers", - ) - .order_by("order") - .distinct() - ) - if workflow_to_run is None - else [workflow_to_run] - ) + workflows = get_workflows_for_trigger(trigger_type, workflow_to_run) for workflow in workflows: if not use_overrides: @@ -1384,13 +716,39 @@ def run_workflows( messages.append(message) if action.type == WorkflowAction.WorkflowActionType.ASSIGNMENT: - assignment_action() + if use_overrides and overrides: + apply_assignment_to_overrides(action, overrides) + else: + apply_assignment_to_document( + action, + document, + doc_tag_ids, + logging_group, + ) elif action.type == WorkflowAction.WorkflowActionType.REMOVAL: - removal_action() + if use_overrides and overrides: + apply_removal_to_overrides(action, overrides) + else: + apply_removal_to_document(action, document, doc_tag_ids) elif action.type == WorkflowAction.WorkflowActionType.EMAIL: - email_action() + context = build_workflow_action_context(document, overrides) + execute_email_action( + action, + document, + context, + logging_group, + original_file, + trigger_type, + ) elif action.type == WorkflowAction.WorkflowActionType.WEBHOOK: - webhook_action() + context = build_workflow_action_context(document, overrides) + execute_webhook_action( + action, + document, + context, + logging_group, + original_file, + ) if not use_overrides: # limit title to 128 characters diff --git a/src/documents/tasks.py b/src/documents/tasks.py index 17bfce3b0..606f278db 100644 --- a/src/documents/tasks.py +++ b/src/documents/tasks.py @@ -41,7 +41,6 @@ from documents.models import DocumentType from documents.models import PaperlessTask from documents.models import StoragePath from documents.models import Tag -from documents.models import Workflow from documents.models import WorkflowRun from documents.models import WorkflowTrigger from documents.parsers import DocumentParser @@ -54,6 +53,7 @@ from documents.sanity_checker import SanityCheckFailedException from documents.signals import document_updated from documents.signals.handlers import cleanup_document_deletion from documents.signals.handlers import run_workflows +from documents.workflows.utils import get_workflows_for_trigger if settings.AUDIT_LOG_ENABLED: from auditlog.models import LogEntry @@ -400,13 +400,8 @@ def check_scheduled_workflows(): Once a document satisfies this condition, and recurring/non-recurring constraints are met, the workflow is run. """ - scheduled_workflows: list[Workflow] = ( - Workflow.objects.filter( - triggers__type=WorkflowTrigger.WorkflowTriggerType.SCHEDULED, - enabled=True, - ) - .distinct() - .prefetch_related("triggers") + scheduled_workflows = get_workflows_for_trigger( + WorkflowTrigger.WorkflowTriggerType.SCHEDULED, ) if scheduled_workflows.count() > 0: logger.debug(f"Checking {len(scheduled_workflows)} scheduled workflows") diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index 6438e4b10..e606bc5a0 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -26,7 +26,7 @@ from rest_framework.test import APITestCase from documents.file_handling import create_source_path_directory from documents.file_handling import generate_unique_filename from documents.signals.handlers import run_workflows -from documents.signals.handlers import send_webhook +from documents.workflows.webhooks import send_webhook if TYPE_CHECKING: from django.db.models import QuerySet @@ -2858,7 +2858,7 @@ class TestWorkflows( mock_email_send.return_value = 1 - with self.assertNoLogs("paperless.handlers", level="ERROR"): + with self.assertNoLogs("paperless.workflows", level="ERROR"): run_workflows( WorkflowTrigger.WorkflowTriggerType.CONSUMPTION, consumable_document, @@ -3096,7 +3096,7 @@ class TestWorkflows( original_filename="sample.pdf", ) - with self.assertLogs("paperless.handlers", level="ERROR") as cm: + with self.assertLogs("paperless.workflows.actions", level="ERROR") as cm: run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) expected_str = "Email backend has not been configured" @@ -3144,7 +3144,7 @@ class TestWorkflows( original_filename="sample.pdf", ) - with self.assertLogs("paperless.handlers", level="ERROR") as cm: + with self.assertLogs("paperless.workflows", level="ERROR") as cm: run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) expected_str = "Error occurred sending email" @@ -3215,7 +3215,7 @@ class TestWorkflows( PAPERLESS_FORCE_SCRIPT_NAME="/paperless", BASE_URL="/paperless/", ) - @mock.patch("documents.signals.handlers.send_webhook.delay") + @mock.patch("documents.workflows.webhooks.send_webhook.delay") def test_workflow_webhook_action_body(self, mock_post): """ GIVEN: @@ -3274,7 +3274,7 @@ class TestWorkflows( @override_settings( PAPERLESS_URL="http://localhost:8000", ) - @mock.patch("documents.signals.handlers.send_webhook.delay") + @mock.patch("documents.workflows.webhooks.send_webhook.delay") def test_workflow_webhook_action_w_files(self, mock_post): """ GIVEN: @@ -3377,7 +3377,7 @@ class TestWorkflows( ) # fails because no file - with self.assertLogs("paperless.handlers", level="ERROR") as cm: + with self.assertLogs("paperless.workflows", level="ERROR") as cm: run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) expected_str = "Error occurred sending webhook" @@ -3420,7 +3420,7 @@ class TestWorkflows( original_filename="sample.pdf", ) - with self.assertLogs("paperless.handlers", level="ERROR") as cm: + with self.assertLogs("paperless.workflows", level="ERROR") as cm: run_workflows(WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, doc) expected_str = "Error occurred parsing webhook params" @@ -3436,7 +3436,7 @@ class TestWorkflows( raise_for_status=mock.Mock(), ) - with self.assertLogs("paperless.handlers") as cm: + with self.assertLogs("paperless.workflows") as cm: send_webhook( url="http://paperless-ngx.com", data="Test message", @@ -3482,7 +3482,7 @@ class TestWorkflows( ), ) - with self.assertLogs("paperless.handlers") as cm: + with self.assertLogs("paperless.workflows") as cm: with self.assertRaises(HTTPStatusError): send_webhook( url="http://paperless-ngx.com", @@ -3498,7 +3498,7 @@ class TestWorkflows( ) self.assertIn(expected_str, cm.output[0]) - @mock.patch("documents.signals.handlers.send_webhook.delay") + @mock.patch("documents.workflows.webhooks.send_webhook.delay") def test_workflow_webhook_action_consumption(self, mock_post): """ GIVEN: diff --git a/src/documents/workflows/__init__.py b/src/documents/workflows/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/documents/workflows/actions.py b/src/documents/workflows/actions.py new file mode 100644 index 000000000..040cbc127 --- /dev/null +++ b/src/documents/workflows/actions.py @@ -0,0 +1,261 @@ +import logging +from pathlib import Path + +from django.conf import settings +from django.contrib.auth.models import User +from django.utils import timezone + +from documents.data_models import ConsumableDocument +from documents.data_models import DocumentMetadataOverrides +from documents.mail import EmailAttachment +from documents.mail import send_email +from documents.models import Correspondent +from documents.models import Document +from documents.models import DocumentType +from documents.models import WorkflowAction +from documents.models import WorkflowTrigger +from documents.templating.workflows import parse_w_workflow_placeholders +from documents.workflows.webhooks import send_webhook + +logger = logging.getLogger("paperless.workflows.actions") + + +def build_workflow_action_context( + document: Document | ConsumableDocument, + overrides: DocumentMetadataOverrides | None, +) -> dict: + """ + Build context dictionary for workflow action placeholder parsing. + """ + use_overrides = overrides is not None + + if not use_overrides: + return { + "title": document.title, + "doc_url": f"{settings.PAPERLESS_URL}{settings.BASE_URL}documents/{document.pk}/", + "correspondent": document.correspondent.name + if document.correspondent + else "", + "document_type": document.document_type.name + if document.document_type + else "", + "owner_username": document.owner.username if document.owner else "", + "filename": document.original_filename or "", + "current_filename": document.filename or "", + "added": timezone.localtime(document.added), + "created": document.created, + } + + correspondent_obj = ( + Correspondent.objects.filter(pk=overrides.correspondent_id).first() + if overrides and overrides.correspondent_id + else None + ) + document_type_obj = ( + DocumentType.objects.filter(pk=overrides.document_type_id).first() + if overrides and overrides.document_type_id + else None + ) + owner_obj = ( + User.objects.filter(pk=overrides.owner_id).first() + if overrides and overrides.owner_id + else None + ) + + filename = document.original_file if document.original_file else "" + return { + "title": overrides.title + if overrides and overrides.title + else str(document.original_file), + "doc_url": "", + "correspondent": correspondent_obj.name if correspondent_obj else "", + "document_type": document_type_obj.name if document_type_obj else "", + "owner_username": owner_obj.username if owner_obj else "", + "filename": filename, + "current_filename": filename, + "added": timezone.localtime(timezone.now()), + "created": overrides.created if overrides else None, + } + + +def execute_email_action( + action: WorkflowAction, + document: Document | ConsumableDocument, + context: dict, + logging_group, + original_file: Path, + trigger_type: WorkflowTrigger.WorkflowTriggerType, +) -> None: + """ + Execute an email action for a workflow. + """ + + if not settings.EMAIL_ENABLED: + logger.error( + "Email backend has not been configured, cannot send email notifications", + extra={"group": logging_group}, + ) + return + + subject = ( + parse_w_workflow_placeholders( + action.email.subject, + context["correspondent"], + context["document_type"], + context["owner_username"], + context["added"], + context["filename"], + context["current_filename"], + context["created"], + context["title"], + context["doc_url"], + ) + if action.email.subject + else "" + ) + body = ( + parse_w_workflow_placeholders( + action.email.body, + context["correspondent"], + context["document_type"], + context["owner_username"], + context["added"], + context["filename"], + context["current_filename"], + context["created"], + context["title"], + context["doc_url"], + ) + if action.email.body + else "" + ) + + try: + attachments: list[EmailAttachment] = [] + if action.email.include_document: + attachment: EmailAttachment | None = None + if trigger_type in [ + WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, + WorkflowTrigger.WorkflowTriggerType.SCHEDULED, + ] and isinstance(document, Document): + friendly_name = ( + Path(context["current_filename"]).name + if context["current_filename"] + else document.source_path.name + ) + attachment = EmailAttachment( + path=document.source_path, + mime_type=document.mime_type, + friendly_name=friendly_name, + ) + elif original_file: + friendly_name = ( + Path(context["current_filename"]).name + if context["current_filename"] + else original_file.name + ) + attachment = EmailAttachment( + path=original_file, + mime_type=document.mime_type, + friendly_name=friendly_name, + ) + if attachment: + attachments = [attachment] + + n_messages = send_email( + subject=subject, + body=body, + to=action.email.to.split(","), + attachments=attachments, + ) + logger.debug( + f"Sent {n_messages} notification email(s) to {action.email.to}", + extra={"group": logging_group}, + ) + except Exception as e: + logger.exception( + f"Error occurred sending notification email: {e}", + extra={"group": logging_group}, + ) + + +def execute_webhook_action( + action: WorkflowAction, + document: Document | ConsumableDocument, + context: dict, + logging_group, + original_file: Path, +): + try: + data = {} + if action.webhook.use_params: + if action.webhook.params: + try: + for key, value in action.webhook.params.items(): + data[key] = parse_w_workflow_placeholders( + value, + context["correspondent"], + context["document_type"], + context["owner_username"], + context["added"], + context["filename"], + context["current_filename"], + context["created"], + context["title"], + context["doc_url"], + ) + except Exception as e: + logger.error( + f"Error occurred parsing webhook params: {e}", + extra={"group": logging_group}, + ) + elif action.webhook.body: + data = parse_w_workflow_placeholders( + action.webhook.body, + context["correspondent"], + context["document_type"], + context["owner_username"], + context["added"], + context["filename"], + context["current_filename"], + context["created"], + context["title"], + context["doc_url"], + ) + headers = {} + if action.webhook.headers: + try: + headers = {str(k): str(v) for k, v in action.webhook.headers.items()} + except Exception as e: + logger.error( + f"Error occurred parsing webhook headers: {e}", + extra={"group": logging_group}, + ) + files = None + if action.webhook.include_document: + with original_file.open("rb") as f: + files = { + "file": ( + str(context["filename"]) + if context["filename"] + else original_file.name, + f.read(), + document.mime_type, + ), + } + send_webhook.delay( + url=action.webhook.url, + data=data, + headers=headers, + files=files, + as_json=action.webhook.as_json, + ) + logger.debug( + f"Webhook to {action.webhook.url} queued", + extra={"group": logging_group}, + ) + except Exception as e: + logger.exception( + f"Error occurred sending webhook: {e}", + extra={"group": logging_group}, + ) diff --git a/src/documents/workflows/mutations.py b/src/documents/workflows/mutations.py new file mode 100644 index 000000000..ef85dba0f --- /dev/null +++ b/src/documents/workflows/mutations.py @@ -0,0 +1,357 @@ +import logging + +from django.utils import timezone +from guardian.shortcuts import remove_perm + +from documents.data_models import DocumentMetadataOverrides +from documents.models import CustomFieldInstance +from documents.models import Document +from documents.models import WorkflowAction +from documents.permissions import set_permissions_for_object +from documents.templating.workflows import parse_w_workflow_placeholders + +logger = logging.getLogger("paperless.workflows.mutations") + + +def apply_assignment_to_document( + action: WorkflowAction, + document: Document, + doc_tag_ids: list[int], + logging_group, +): + """ + Apply assignment actions to a Document instance. + + action: WorkflowAction, annotated with 'has_assign_*' boolean fields + """ + if action.has_assign_tags: + tag_ids_to_add: set[int] = set() + for tag in action.assign_tags.all(): + tag_ids_to_add.add(tag.pk) + tag_ids_to_add.update(int(pk) for pk in tag.get_ancestors_pks()) + + doc_tag_ids[:] = list(set(doc_tag_ids) | tag_ids_to_add) + + if action.assign_correspondent: + document.correspondent = action.assign_correspondent + + if action.assign_document_type: + document.document_type = action.assign_document_type + + if action.assign_storage_path: + document.storage_path = action.assign_storage_path + + if action.assign_owner: + document.owner = action.assign_owner + + if action.assign_title: + try: + document.title = parse_w_workflow_placeholders( + action.assign_title, + document.correspondent.name if document.correspondent else "", + document.document_type.name if document.document_type else "", + document.owner.username if document.owner else "", + timezone.localtime(document.added), + document.original_filename or "", + document.filename or "", + document.created, + ) + except Exception: # pragma: no cover + logger.exception( + f"Error occurred parsing title assignment '{action.assign_title}', falling back to original", + extra={"group": logging_group}, + ) + + if any( + [ + action.has_assign_view_users, + action.has_assign_view_groups, + action.has_assign_change_users, + action.has_assign_change_groups, + ], + ): + permissions = { + "view": { + "users": action.assign_view_users.values_list("id", flat=True), + "groups": action.assign_view_groups.values_list("id", flat=True), + }, + "change": { + "users": action.assign_change_users.values_list("id", flat=True), + "groups": action.assign_change_groups.values_list("id", flat=True), + }, + } + set_permissions_for_object( + permissions=permissions, + object=document, + merge=True, + ) + + if action.has_assign_custom_fields: + for field in action.assign_custom_fields.all(): + value_field_name = CustomFieldInstance.get_value_field_name( + data_type=field.data_type, + ) + args = { + value_field_name: action.assign_custom_fields_values.get( + str(field.pk), + None, + ), + } + # for some reason update_or_create doesn't work here + instance = CustomFieldInstance.objects.filter( + field=field, + document=document, + ).first() + if instance and args[value_field_name] is not None: + setattr(instance, value_field_name, args[value_field_name]) + instance.save() + elif not instance: + CustomFieldInstance.objects.create( + **args, + field=field, + document=document, + ) + + +def apply_assignment_to_overrides( + action: WorkflowAction, + overrides: DocumentMetadataOverrides, +): + """ + Apply assignment actions to DocumentMetadataOverrides. + + action: WorkflowAction, annotated with 'has_assign_*' boolean fields + """ + if action.has_assign_tags: + if overrides.tag_ids is None: + overrides.tag_ids = [] + tag_ids_to_add: set[int] = set() + for tag in action.assign_tags.all(): + tag_ids_to_add.add(tag.pk) + tag_ids_to_add.update(int(pk) for pk in tag.get_ancestors_pks()) + + overrides.tag_ids = list(set(overrides.tag_ids) | tag_ids_to_add) + + if action.assign_correspondent: + overrides.correspondent_id = action.assign_correspondent.pk + + if action.assign_document_type: + overrides.document_type_id = action.assign_document_type.pk + + if action.assign_storage_path: + overrides.storage_path_id = action.assign_storage_path.pk + + if action.assign_owner: + overrides.owner_id = action.assign_owner.pk + + if action.assign_title: + overrides.title = action.assign_title + + if any( + [ + action.has_assign_view_users, + action.has_assign_view_groups, + action.has_assign_change_users, + action.has_assign_change_groups, + ], + ): + overrides.view_users = list( + set( + (overrides.view_users or []) + + list(action.assign_view_users.values_list("id", flat=True)), + ), + ) + overrides.view_groups = list( + set( + (overrides.view_groups or []) + + list(action.assign_view_groups.values_list("id", flat=True)), + ), + ) + overrides.change_users = list( + set( + (overrides.change_users or []) + + list(action.assign_change_users.values_list("id", flat=True)), + ), + ) + overrides.change_groups = list( + set( + (overrides.change_groups or []) + + list(action.assign_change_groups.values_list("id", flat=True)), + ), + ) + + if action.has_assign_custom_fields: + if overrides.custom_fields is None: + overrides.custom_fields = {} + overrides.custom_fields.update( + { + field.pk: action.assign_custom_fields_values.get( + str(field.pk), + None, + ) + for field in action.assign_custom_fields.all() + }, + ) + + +def apply_removal_to_document( + action: WorkflowAction, + document: Document, + doc_tag_ids: list[int], +): + """ + Apply removal actions to a Document instance. + + action: WorkflowAction, annotated with 'has_remove_*' boolean fields + """ + + if action.remove_all_tags: + doc_tag_ids.clear() + else: + tag_ids_to_remove: set[int] = set() + for tag in action.remove_tags.all(): + tag_ids_to_remove.add(tag.pk) + tag_ids_to_remove.update(int(pk) for pk in tag.get_descendants_pks()) + + doc_tag_ids[:] = [t for t in doc_tag_ids if t not in tag_ids_to_remove] + + if action.remove_all_correspondents or ( + document.correspondent + and action.remove_correspondents.filter(pk=document.correspondent.pk).exists() + ): + document.correspondent = None + + if action.remove_all_document_types or ( + document.document_type + and action.remove_document_types.filter(pk=document.document_type.pk).exists() + ): + document.document_type = None + + if action.remove_all_storage_paths or ( + document.storage_path + and action.remove_storage_paths.filter(pk=document.storage_path.pk).exists() + ): + document.storage_path = None + + if action.remove_all_owners or ( + document.owner and action.remove_owners.filter(pk=document.owner.pk).exists() + ): + document.owner = None + + if action.remove_all_permissions: + permissions = { + "view": {"users": [], "groups": []}, + "change": {"users": [], "groups": []}, + } + set_permissions_for_object( + permissions=permissions, + object=document, + merge=False, + ) + + if any( + [ + action.has_remove_view_users, + action.has_remove_view_groups, + action.has_remove_change_users, + action.has_remove_change_groups, + ], + ): + for user in action.remove_view_users.all(): + remove_perm("view_document", user, document) + for user in action.remove_change_users.all(): + remove_perm("change_document", user, document) + for group in action.remove_view_groups.all(): + remove_perm("view_document", group, document) + for group in action.remove_change_groups.all(): + remove_perm("change_document", group, document) + + if action.remove_all_custom_fields: + CustomFieldInstance.objects.filter(document=document).hard_delete() + elif action.has_remove_custom_fields: + CustomFieldInstance.objects.filter( + field__in=action.remove_custom_fields.all(), + document=document, + ).hard_delete() + + +def apply_removal_to_overrides( + action: WorkflowAction, + overrides: DocumentMetadataOverrides, +): + """ + Apply removal actions to DocumentMetadataOverrides. + + action: WorkflowAction, annotated with 'has_remove_*' boolean fields + """ + if action.remove_all_tags: + overrides.tag_ids = None + elif overrides.tag_ids: + tag_ids_to_remove: set[int] = set() + for tag in action.remove_tags.all(): + tag_ids_to_remove.add(tag.pk) + tag_ids_to_remove.update(int(pk) for pk in tag.get_descendants_pks()) + + overrides.tag_ids = [t for t in overrides.tag_ids if t not in tag_ids_to_remove] + + if action.remove_all_correspondents or ( + overrides.correspondent_id + and action.remove_correspondents.filter(pk=overrides.correspondent_id).exists() + ): + overrides.correspondent_id = None + + if action.remove_all_document_types or ( + overrides.document_type_id + and action.remove_document_types.filter(pk=overrides.document_type_id).exists() + ): + overrides.document_type_id = None + + if action.remove_all_storage_paths or ( + overrides.storage_path_id + and action.remove_storage_paths.filter(pk=overrides.storage_path_id).exists() + ): + overrides.storage_path_id = None + + if action.remove_all_owners or ( + overrides.owner_id + and action.remove_owners.filter(pk=overrides.owner_id).exists() + ): + overrides.owner_id = None + + if action.remove_all_permissions: + overrides.view_users = None + overrides.view_groups = None + overrides.change_users = None + overrides.change_groups = None + elif any( + [ + action.has_remove_view_users, + action.has_remove_view_groups, + action.has_remove_change_users, + action.has_remove_change_groups, + ], + ): + if overrides.view_users: + for user in action.remove_view_users.filter(pk__in=overrides.view_users): + overrides.view_users.remove(user.pk) + if overrides.change_users: + for user in action.remove_change_users.filter( + pk__in=overrides.change_users, + ): + overrides.change_users.remove(user.pk) + if overrides.view_groups: + for group in action.remove_view_groups.filter(pk__in=overrides.view_groups): + overrides.view_groups.remove(group.pk) + if overrides.change_groups: + for group in action.remove_change_groups.filter( + pk__in=overrides.change_groups, + ): + overrides.change_groups.remove(group.pk) + + if action.remove_all_custom_fields: + overrides.custom_fields = None + elif action.has_remove_custom_fields and overrides.custom_fields: + for field in action.remove_custom_fields.filter( + pk__in=overrides.custom_fields.keys(), + ): + overrides.custom_fields.pop(field.pk, None) diff --git a/src/documents/workflows/utils.py b/src/documents/workflows/utils.py new file mode 100644 index 000000000..553622252 --- /dev/null +++ b/src/documents/workflows/utils.py @@ -0,0 +1,116 @@ +import logging + +from django.db.models import Exists +from django.db.models import OuterRef +from django.db.models import Prefetch + +from documents.models import Workflow +from documents.models import WorkflowAction +from documents.models import WorkflowTrigger + +logger = logging.getLogger("paperless.workflows") + + +def get_workflows_for_trigger( + trigger_type: WorkflowTrigger.WorkflowTriggerType, + workflow_to_run: Workflow | None = None, +): + """ + Return workflows relevant to a trigger. If a specific workflow is given, + wrap it in a list; otherwise fetch enabled workflows for the trigger with + the prefetches used by the runner. + """ + if workflow_to_run is not None: + return [workflow_to_run] + + annotated_actions = ( + WorkflowAction.objects.select_related( + "assign_correspondent", + "assign_document_type", + "assign_storage_path", + "assign_owner", + "email", + "webhook", + ) + .prefetch_related( + "assign_tags", + "assign_view_users", + "assign_view_groups", + "assign_change_users", + "assign_change_groups", + "assign_custom_fields", + "remove_tags", + "remove_correspondents", + "remove_document_types", + "remove_storage_paths", + "remove_custom_fields", + "remove_owners", + ) + .annotate( + has_assign_tags=Exists( + WorkflowAction.assign_tags.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_assign_view_users=Exists( + WorkflowAction.assign_view_users.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_assign_view_groups=Exists( + WorkflowAction.assign_view_groups.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_assign_change_users=Exists( + WorkflowAction.assign_change_users.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_assign_change_groups=Exists( + WorkflowAction.assign_change_groups.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_assign_custom_fields=Exists( + WorkflowAction.assign_custom_fields.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_remove_view_users=Exists( + WorkflowAction.remove_view_users.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_remove_view_groups=Exists( + WorkflowAction.remove_view_groups.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_remove_change_users=Exists( + WorkflowAction.remove_change_users.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_remove_change_groups=Exists( + WorkflowAction.remove_change_groups.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + has_remove_custom_fields=Exists( + WorkflowAction.remove_custom_fields.through.objects.filter( + workflowaction_id=OuterRef("pk"), + ), + ), + ) + ) + + return ( + Workflow.objects.filter(enabled=True, triggers__type=trigger_type) + .prefetch_related( + Prefetch("actions", queryset=annotated_actions), + "triggers", + ) + .order_by("order") + .distinct() + ) diff --git a/src/documents/workflows/webhooks.py b/src/documents/workflows/webhooks.py new file mode 100644 index 000000000..c7bb9f7c2 --- /dev/null +++ b/src/documents/workflows/webhooks.py @@ -0,0 +1,96 @@ +import ipaddress +import logging +import socket +from urllib.parse import urlparse + +import httpx +from celery import shared_task +from django.conf import settings + +logger = logging.getLogger("paperless.workflows.webhooks") + + +def _is_public_ip(ip: str) -> bool: + try: + obj = ipaddress.ip_address(ip) + return not ( + obj.is_private + or obj.is_loopback + or obj.is_link_local + or obj.is_multicast + or obj.is_unspecified + ) + except ValueError: # pragma: no cover + return False + + +def _resolve_first_ip(host: str) -> str | None: + try: + info = socket.getaddrinfo(host, None) + return info[0][4][0] if info else None + except Exception: # pragma: no cover + return None + + +@shared_task( + retry_backoff=True, + autoretry_for=(httpx.HTTPStatusError,), + max_retries=3, + throws=(httpx.HTTPError,), +) +def send_webhook( + url: str, + data: str | dict, + headers: dict, + files: dict, + *, + as_json: bool = False, +): + p = urlparse(url) + if p.scheme.lower() not in settings.WEBHOOKS_ALLOWED_SCHEMES or not p.hostname: + logger.warning("Webhook blocked: invalid scheme/hostname") + raise ValueError("Invalid URL scheme or hostname.") + + port = p.port or (443 if p.scheme == "https" else 80) + if ( + len(settings.WEBHOOKS_ALLOWED_PORTS) > 0 + and port not in settings.WEBHOOKS_ALLOWED_PORTS + ): + logger.warning("Webhook blocked: port not permitted") + raise ValueError("Destination port not permitted.") + + ip = _resolve_first_ip(p.hostname) + if not ip or ( + not _is_public_ip(ip) and not settings.WEBHOOKS_ALLOW_INTERNAL_REQUESTS + ): + logger.warning("Webhook blocked: destination not allowed") + raise ValueError("Destination host is not allowed.") + + try: + post_args = { + "url": url, + "headers": { + k: v for k, v in (headers or {}).items() if k.lower() != "host" + }, + "files": files or None, + "timeout": 5.0, + "follow_redirects": False, + } + if as_json: + post_args["json"] = data + elif isinstance(data, dict): + post_args["data"] = data + else: + post_args["content"] = data + + httpx.post( + **post_args, + ).raise_for_status() + logger.info( + f"Webhook sent to {url}", + ) + except Exception as e: + logger.error( + f"Failed attempt sending webhook to {url}: {e}", + ) + raise e