diff --git a/.github/workflows/ci-backend.yml b/.github/workflows/ci-backend.yml index 7ca37699e..d771c3115 100644 --- a/.github/workflows/ci-backend.yml +++ b/.github/workflows/ci-backend.yml @@ -24,7 +24,7 @@ jobs: backend_changed: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.backend == 'true' }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false @@ -87,7 +87,7 @@ jobs: fail-fast: false steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Start containers @@ -96,11 +96,11 @@ jobs: docker compose --file docker/compose/docker-compose.ci-test.yml up --detach - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "${{ matrix.python-version }}" - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true @@ -141,13 +141,13 @@ jobs: pytest - name: Upload test results to Codecov if: always() - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: flags: backend-python-${{ matrix.python-version }} files: junit.xml report_type: test_results - name: Upload coverage to Codecov - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: flags: backend-python-${{ matrix.python-version }} files: coverage.xml @@ -169,16 +169,16 @@ jobs: PAPERLESS_SECRET_KEY: "ci-typing-not-a-real-secret" steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "${{ env.DEFAULT_PYTHON }}" - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true @@ -202,7 +202,7 @@ jobs: check \ src/ - name: Cache Mypy - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: .mypy_cache # Keyed by OS, Python version, and dependency hashes diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index db747ad33..39cf1b5db 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -41,7 +41,7 @@ jobs: ref-name: ${{ steps.ref.outputs.name }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Determine ref name diff --git a/.github/workflows/ci-docs.yml b/.github/workflows/ci-docs.yml index 8961baa3f..5bfa6f970 100644 --- a/.github/workflows/ci-docs.yml +++ b/.github/workflows/ci-docs.yml @@ -21,7 +21,7 @@ jobs: docs_changed: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.docs == 'true' }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false @@ -69,16 +69,16 @@ jobs: steps: - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true diff --git a/.github/workflows/ci-frontend.yml b/.github/workflows/ci-frontend.yml index 054a1ce3a..ed9d28011 100644 --- a/.github/workflows/ci-frontend.yml +++ b/.github/workflows/ci-frontend.yml @@ -21,7 +21,7 @@ jobs: frontend_changed: ${{ steps.force.outputs.run_all == 'true' || steps.filter.outputs.frontend == 'true' }} steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false @@ -77,11 +77,11 @@ jobs: contents: read steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -92,7 +92,7 @@ jobs: cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies id: cache-frontend-deps - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store @@ -109,11 +109,11 @@ jobs: contents: read steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -123,7 +123,7 @@ jobs: cache: 'pnpm' cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store @@ -148,11 +148,11 @@ jobs: shard-count: [4] steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -162,7 +162,7 @@ jobs: cache: 'pnpm' cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store @@ -174,13 +174,13 @@ jobs: run: cd src-ui && pnpm run test --max-workers=2 --shard=${{ matrix.shard-index }}/${{ matrix.shard-count }} - name: Upload test results to Codecov if: always() - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: flags: frontend-node-${{ matrix.node-version }} directory: src-ui/ report_type: test_results - name: Upload coverage to Codecov - uses: codecov/codecov-action@e79a6962e0d4c0c17b229090214935d2e33f8354 # v6.0.1 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: flags: frontend-node-${{ matrix.node-version }} directory: src-ui/coverage/ @@ -191,7 +191,7 @@ jobs: runs-on: ubuntu-24.04 permissions: contents: read - container: mcr.microsoft.com/playwright:v1.60.0-noble + container: mcr.microsoft.com/playwright:v1.61.1-noble env: PLAYWRIGHT_BROWSERS_PATH: /ms-playwright PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 @@ -203,11 +203,11 @@ jobs: shard-count: [2] steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -217,7 +217,7 @@ jobs: cache: 'pnpm' cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store @@ -239,12 +239,12 @@ jobs: contents: read steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 2 persist-credentials: false - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -254,7 +254,7 @@ jobs: cache: 'pnpm' cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store diff --git a/.github/workflows/ci-lint.yml b/.github/workflows/ci-lint.yml index 3205bcd78..bfe823e09 100644 --- a/.github/workflows/ci-lint.yml +++ b/.github/workflows/ci-lint.yml @@ -17,11 +17,11 @@ jobs: runs-on: ubuntu-slim steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Install Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: "3.14" - name: Run prek diff --git a/.github/workflows/ci-release.yml b/.github/workflows/ci-release.yml index eee5744ee..bdc8969e1 100644 --- a/.github/workflows/ci-release.yml +++ b/.github/workflows/ci-release.yml @@ -20,7 +20,7 @@ jobs: statuses: read steps: - name: Wait for Docker build - uses: lewagon/wait-on-check-action@9312864dfbc9fd208e9c0417843430751c042800 # v1.7.0 + uses: lewagon/wait-on-check-action@96d9100b431964d10e0136aff8b9ccb92470505e # v1.8.0 with: ref: ${{ github.sha }} check-name: 'Merge and Push Manifest' @@ -34,12 +34,12 @@ jobs: contents: read steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false # ---- Frontend Build ---- - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -54,11 +54,11 @@ jobs: # ---- Backend Setup ---- - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: false @@ -170,7 +170,7 @@ jobs: fi - name: Create release and changelog id: create-release - uses: release-drafter/release-drafter@693d20e7c1ce1a81d3a41962f85914253b518449 # v7.3.1 + uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1 with: name: Paperless-ngx ${{ steps.get-version.outputs.version }} tag: ${{ steps.get-version.outputs.version }} @@ -181,7 +181,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload release archive - uses: shogo82148/actions-upload-release-asset@ee2ae851dc5d938b90075b3ef12c540abfd1ee72 # v1.10.1 + uses: shogo82148/actions-upload-release-asset@394b3c11c3cfc038b5396ad265c074065cf875c3 # v1.10.2 with: github_token: ${{ secrets.GITHUB_TOKEN }} upload_url: ${{ steps.create-release.outputs.upload_url }} @@ -201,17 +201,17 @@ jobs: pull-requests: write steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: ref: main persist-credentials: true # for pushing changelog branch - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: python-version: ${{ env.DEFAULT_PYTHON_VERSION }} - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: false diff --git a/.github/workflows/ci-static-analysis.yml b/.github/workflows/ci-static-analysis.yml index 4d108b5bf..395a71eb4 100644 --- a/.github/workflows/ci-static-analysis.yml +++ b/.github/workflows/ci-static-analysis.yml @@ -22,11 +22,11 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Run zizmor - uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6 + uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 semgrep: name: Semgrep CE runs-on: ubuntu-24.04 @@ -38,13 +38,13 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Run Semgrep run: semgrep scan --config auto --sarif-output results.sarif - name: Upload results to GitHub code scanning - uses: github/codeql-action/upload-sarif@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 if: always() with: sarif_file: results.sarif diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b0fbb5b40..639e7d609 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -34,12 +34,12 @@ jobs: # Learn more about CodeQL language support at https://git.io/codeql-language-support steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -47,4 +47,4 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0 + uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2 diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 9a45e3077..4cd03dd9a 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -17,12 +17,12 @@ jobs: environment: translation-sync steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: token: ${{ secrets.PNGX_BOT_PAT }} persist-credentials: false - name: crowdin action - uses: crowdin/github-action@8868a33591d21088edfc398968173a3b98d51706 # v2.16.2 + uses: crowdin/github-action@52aa776766211d83d975df51f3b9c53c2f8ba35f # v2.16.3 with: upload_translations: false download_translations: true diff --git a/.github/workflows/project-actions.yml b/.github/workflows/project-actions.yml index e969474b6..6d251c71c 100644 --- a/.github/workflows/project-actions.yml +++ b/.github/workflows/project-actions.yml @@ -19,6 +19,6 @@ jobs: if: github.event_name == 'pull_request_target' && (github.event.action == 'opened' || github.event.action == 'reopened') && github.event.pull_request.user.login != 'dependabot' steps: - name: Label PR with release-drafter - uses: release-drafter/release-drafter@693d20e7c1ce1a81d3a41962f85914253b518449 # v7.3.1 + uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/translate-strings.yml b/.github/workflows/translate-strings.yml index 9d8d97fa3..6002b1cd5 100644 --- a/.github/workflows/translate-strings.yml +++ b/.github/workflows/translate-strings.yml @@ -14,7 +14,7 @@ jobs: contents: write steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 env: GH_REF: ${{ github.ref }} # sonar rule:githubactions:S7630 - avoid injection with: @@ -23,13 +23,13 @@ jobs: persist-credentials: true # for pushing translation branch - name: Set up Python id: setup-python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 - name: Install system dependencies run: | sudo apt-get update -qq sudo apt-get install -qq --no-install-recommends gettext - name: Install uv - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + uses: astral-sh/setup-uv@fac544c07dec837d0ccb6301d7b5580bf5edae39 # v8.2.0 with: version: ${{ env.DEFAULT_UV_VERSION }} enable-cache: true @@ -43,7 +43,7 @@ jobs: PAPERLESS_SECRET_KEY: "ci-translate-not-a-real-secret" run: cd src/ && uv run manage.py makemessages -l en_US -i "samples*" - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 with: version: 10 - name: Use Node.js 24 @@ -54,7 +54,7 @@ jobs: cache-dependency-path: 'src-ui/pnpm-lock.yaml' - name: Cache frontend dependencies id: cache-frontend-deps - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 with: path: | ~/.pnpm-store diff --git a/SECURITY.md b/SECURITY.md index 14e6d43ac..a7eafcc40 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -63,6 +63,7 @@ The following are not generally considered vulnerabilities unless accompanied by - optional webhook, mail, AI, OCR, or integration behavior described without a product-level vulnerability - missing limits or hardening settings presented without concrete impact - generic AI or static-analysis output that is not confirmed against the current codebase and a real deployment scenario +- the ability to attach objects that a user cannot access to a document by ID is an intentional design choice, and not considered a vulnerability ## Transparency diff --git a/src-ui/angular.json b/src-ui/angular.json index 9ed7488b6..391dcc710 100644 --- a/src-ui/angular.json +++ b/src-ui/angular.json @@ -155,7 +155,9 @@ "builder": "@angular-builders/jest:run", "options": { "tsConfig": "tsconfig.spec.json", - "zoneless": false + "zoneless": true, + "watch": false, + "coverage": true } }, "lint": { diff --git a/src-ui/eslint.config.js b/src-ui/eslint.config.js index 6ee2e4de8..20922c1eb 100644 --- a/src-ui/eslint.config.js +++ b/src-ui/eslint.config.js @@ -3,6 +3,27 @@ const angularTemplatePlugin = require('@angular-eslint/eslint-plugin-template') const angularTemplateParser = require('@angular-eslint/template-parser') const tsParser = require('@typescript-eslint/parser') +const angularTsRecommendedRules = { + '@angular-eslint/contextual-lifecycle': 'error', + '@angular-eslint/no-empty-lifecycle-method': 'error', + '@angular-eslint/no-input-rename': 'error', + '@angular-eslint/no-inputs-metadata-property': 'error', + '@angular-eslint/no-output-native': 'error', + '@angular-eslint/no-output-on-prefix': 'error', + '@angular-eslint/no-output-rename': 'error', + '@angular-eslint/no-outputs-metadata-property': 'error', + '@angular-eslint/prefer-inject': 'error', + '@angular-eslint/prefer-standalone': 'error', + '@angular-eslint/use-pipe-transform-interface': 'error', + '@angular-eslint/use-lifecycle-interface': 'warn', +} +const angularTemplateRecommendedRules = { + '@angular-eslint/template/banana-in-box': 'error', + '@angular-eslint/template/eqeqeq': 'error', + '@angular-eslint/template/no-negated-async': 'error', + '@angular-eslint/template/prefer-control-flow': 'error', +} + module.exports = [ { ignores: ['projects/**/*', 'src/app/components/common/pdf-viewer/**'], @@ -12,8 +33,6 @@ module.exports = [ languageOptions: { parser: tsParser, parserOptions: { - project: ['tsconfig.json'], - createDefaultProgram: true, ecmaVersion: 2020, sourceType: 'module', }, @@ -22,9 +41,9 @@ module.exports = [ '@angular-eslint': angularEslintPlugin, '@angular-eslint/template': angularTemplatePlugin, }, - processor: '@angular-eslint/template/extract-inline-html', + processor: angularTemplatePlugin.processors['extract-inline-html'], rules: { - ...angularEslintPlugin.configs.recommended.rules, + ...angularTsRecommendedRules, '@angular-eslint/directive-selector': [ 'error', { @@ -52,7 +71,7 @@ module.exports = [ '@angular-eslint/template': angularTemplatePlugin, }, rules: { - ...angularTemplatePlugin.configs.recommended.rules, + ...angularTemplateRecommendedRules, }, }, ] diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 5949d2357..9ec4f0cfd 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -5,236 +5,236 @@ Close - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/alert/alert.ts - 50 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/alert/alert.ts + 39 Slide of - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/carousel/carousel.ts - 131,135 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/carousel/carousel.ts + 129,133 Currently selected slide number read by screen reader Previous - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/carousel/carousel.ts - 159,162 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/carousel/carousel.ts + 160,161 Next - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/carousel/carousel.ts - 202,203 - - - - Select month - - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation-select.ts - 91 - - - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation-select.ts - 91 - - - - Select year - - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation-select.ts - 91 - - - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation-select.ts - 91 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/carousel/carousel.ts + 205,206 Previous month - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation.ts - 83,85 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/datepicker-navigation.ts + 74,76 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation.ts - 112 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/datepicker-navigation.ts + 103 Next month - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation.ts - 112 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/datepicker-navigation.ts + 103 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/datepicker/datepicker-navigation.ts - 112 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/datepicker-navigation.ts + 103 + + + + Select month + + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/ngb-calendar.ts + 63,65 + + + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/ngb-calendar.ts + 106,107 + + + + Select year + + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/ngb-calendar.ts + 145,146 + + + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/datepicker/ngb-calendar.ts + 179,183 «« - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 302 « - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 333,335 » - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 375 »» - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 408 First - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 437 Previous - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 472,474 Next - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 506 Last - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/pagination/pagination-config.ts - 20 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/pagination/pagination.ts + 506 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/progressbar/progressbar.ts - 41,42 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/progressbar/progressbar.ts + 40,41 HH - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 263,264 Hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 301,302 MM - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 341,342 Minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 381,384 Increment hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 429,430 Decrement hours - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Increment minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Decrement minutes - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 SS - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Increment seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Decrement seconds - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/timepicker/timepicker-config.ts - 21 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/timepicker/timepicker.ts + 442 Close - node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@20.0.0_@angular+common@21.2.17_@angular+core@21.2.17_@angula_c73b7cd709f0477f7304a9bb13ff4be6/node_modules/src/toast/toast-config.ts - 54 + node_modules/.pnpm/@ng-bootstrap+ng-bootstrap@21.0.0_@angular+common@22.0.5_@angular+core@22.0.5_@angular+_d3d748c9cb3098b2696b844e4082b488/node_modules/src/toast/toast.ts + 116 @@ -256,19 +256,19 @@ src/app/components/admin/trash/trash.component.ts - 146 + 145 src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 44 + 60 src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 47 + 63 src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 50 + 66 src/app/components/dashboard/widgets/upload-file-widget/upload-file-widget.component.html @@ -768,7 +768,7 @@ src/app/components/admin/tasks/tasks.component.html - 16 + 19 @@ -783,7 +783,7 @@ src/app/components/admin/tasks/tasks.component.html - 23 + 26 src/app/components/admin/trash/trash.component.html @@ -827,11 +827,11 @@ src/app/components/dashboard/widgets/widget-frame/widget-frame.component.html - 18 + 19 src/app/components/document-detail/document-detail.component.html - 465 + 464 src/app/components/document-list/document-list.component.html @@ -1041,7 +1041,7 @@ src/app/components/app-frame/global-search/global-search.component.ts - 122 + 124 @@ -1277,7 +1277,7 @@ src/app/components/document-detail/document-detail.component.ts - 1762 + 1787 @@ -1578,21 +1578,21 @@ Use system language src/app/components/admin/settings/settings.component.ts - 79 + 80 Use date format of display language src/app/components/admin/settings/settings.component.ts - 82 + 83 Archive serial number src/app/components/admin/settings/settings.component.ts - 96 + 97 src/app/components/document-detail/document-detail.component.html @@ -1603,7 +1603,7 @@ Correspondent src/app/components/admin/settings/settings.component.ts - 98 + 99 src/app/components/document-detail/document-detail.component.html @@ -1634,7 +1634,7 @@ Document type src/app/components/admin/settings/settings.component.ts - 99 + 100 src/app/components/document-detail/document-detail.component.html @@ -1665,7 +1665,7 @@ Storage path src/app/components/admin/settings/settings.component.ts - 100 + 101 src/app/components/document-detail/document-detail.component.html @@ -1692,7 +1692,7 @@ Tags src/app/components/admin/settings/settings.component.ts - 101 + 102 src/app/components/app-frame/app-frame.component.html @@ -1700,7 +1700,7 @@ src/app/components/common/input/tags/tags.component.ts - 80 + 86 src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.html @@ -1724,7 +1724,7 @@ src/app/components/manage/document-attributes/document-attributes.component.ts - 89 + 85 src/app/data/document.ts @@ -1735,7 +1735,7 @@ Error retrieving users src/app/components/admin/settings/settings.component.ts - 252 + 255 src/app/components/admin/users-groups/users-groups.component.ts @@ -1746,7 +1746,7 @@ Error retrieving groups src/app/components/admin/settings/settings.component.ts - 271 + 274 src/app/components/admin/users-groups/users-groups.component.ts @@ -1757,40 +1757,40 @@ Settings were saved successfully. src/app/components/admin/settings/settings.component.ts - 588 + 591 Settings were saved successfully. Reload is required to apply some changes. src/app/components/admin/settings/settings.component.ts - 592 + 595 Reload now src/app/components/admin/settings/settings.component.ts - 593 + 596 An error occurred while saving settings. src/app/components/admin/settings/settings.component.ts - 603 + 606 src/app/components/app-frame/app-frame.component.ts - 164 + 160 src/app/components/app-frame/app-frame.component.ts - 238 + 249 src/app/components/app-frame/app-frame.component.ts - 263 + 275 @@ -1830,11 +1830,18 @@ 147 + + Dismiss all + + src/app/components/admin/tasks/tasks.component.html + 15 + + All src/app/components/admin/tasks/tasks.component.html - 36 + 39 src/app/components/admin/tasks/tasks.component.ts @@ -1842,7 +1849,7 @@ src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 151 + 154 src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -1873,36 +1880,36 @@ Filter by src/app/components/admin/tasks/tasks.component.html - 56 + 59 All types src/app/components/admin/tasks/tasks.component.html - 61 + 64 src/app/components/admin/tasks/tasks.component.ts - 209 + 215 All sources src/app/components/admin/tasks/tasks.component.html - 70 + 73 src/app/components/admin/tasks/tasks.component.ts - 221 + 227 Reset filters src/app/components/admin/tasks/tasks.component.html - 101 + 104 src/app/components/document-list/document-list.component.html @@ -1917,14 +1924,14 @@ {VAR_PLURAL, plural, =1 {1 task} other { tasks}} src/app/components/admin/tasks/tasks.component.html - 122 + 125 Name src/app/components/admin/tasks/tasks.component.html - 144 + 147 src/app/components/admin/tasks/tasks.component.ts @@ -2035,7 +2042,7 @@ Created src/app/components/admin/tasks/tasks.component.html - 145 + 148 src/app/components/common/dates-dropdown/dates-dropdown.component.html @@ -2043,7 +2050,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 94 + 95 src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html @@ -2066,21 +2073,21 @@ Results src/app/components/admin/tasks/tasks.component.html - 147 + 150 Info src/app/components/admin/tasks/tasks.component.html - 149 + 152 Actions src/app/components/admin/tasks/tasks.component.html - 150 + 153 src/app/components/admin/trash/trash.component.html @@ -2151,22 +2158,26 @@ click for full output src/app/components/admin/tasks/tasks.component.html - 201 + 204 Dismiss src/app/components/admin/tasks/tasks.component.html - 214 + 217 src/app/components/admin/tasks/tasks.component.ts - 310 + 317 + + + src/app/components/admin/tasks/tasks.component.ts + 351 src/app/components/document-detail/document-detail.component.ts - 637 + 653 src/app/components/document-detail/document-version-dropdown/document-version-dropdown.component.html @@ -2177,28 +2188,28 @@ Open Document src/app/components/admin/tasks/tasks.component.html - 219 + 222 Result message src/app/components/admin/tasks/tasks.component.html - 232 + 235 Duplicate src/app/components/admin/tasks/tasks.component.html - 239 + 242 Open src/app/components/admin/tasks/tasks.component.html - 247 + 250 src/app/components/app-frame/global-search/global-search.component.html @@ -2229,21 +2240,21 @@ Input data src/app/components/admin/tasks/tasks.component.html - 257 + 260 Result data src/app/components/admin/tasks/tasks.component.html - 263 + 266 No tasks match the current filters. src/app/components/admin/tasks/tasks.component.html - 285 + 288 @@ -2303,7 +2314,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 79 + 80 @@ -2363,7 +2374,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 121 + 122 @@ -2374,7 +2385,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 83 + 84 @@ -2385,7 +2396,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 75 + 76 @@ -2420,60 +2431,78 @@ Dismiss selected src/app/components/admin/tasks/tasks.component.ts - 233 + 239 Dismiss visible src/app/components/admin/tasks/tasks.component.ts - 234 + 240 Confirm Dismiss src/app/components/admin/tasks/tasks.component.ts - 307 + 314 Dismiss tasks? src/app/components/admin/tasks/tasks.component.ts - 308 + 315 Error dismissing tasks src/app/components/admin/tasks/tasks.component.ts - 319 + 326 + + + src/app/components/admin/tasks/tasks.component.ts + 360 Error dismissing task src/app/components/admin/tasks/tasks.component.ts - 331 + 338 + + + + Confirm Dismiss All + + src/app/components/admin/tasks/tasks.component.ts + 348 + + + + Dismiss all tasks? + + src/app/components/admin/tasks/tasks.component.ts + 349 Success. New document id created src/app/components/admin/tasks/tasks.component.ts - 377 + 408 Duplicate of document # src/app/components/admin/tasks/tasks.component.ts - 387 + 418 src/app/components/admin/tasks/tasks.component.ts - 421 + 452 @@ -2564,11 +2593,11 @@ src/app/components/admin/trash/trash.component.ts - 82 + 81 src/app/components/admin/trash/trash.component.ts - 116 + 115 src/app/components/admin/users-groups/users-groups.component.html @@ -2584,7 +2613,7 @@ src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 87 + 88 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -2660,7 +2689,7 @@ src/app/components/manage/document-attributes/management-list/management-list.component.ts - 276 + 275 src/app/components/manage/mail/mail.component.html @@ -2702,37 +2731,37 @@ Confirm delete src/app/components/admin/trash/trash.component.ts - 78 + 77 src/app/components/admin/trash/trash.component.ts - 110 + 109 src/app/components/manage/document-attributes/management-list/management-list.component.ts - 272 + 271 src/app/components/manage/document-attributes/management-list/management-list.component.ts - 453 + 452 This operation will permanently delete this document. src/app/components/admin/trash/trash.component.ts - 79 + 78 This operation cannot be undone. src/app/components/admin/trash/trash.component.ts - 80 + 79 src/app/components/admin/trash/trash.component.ts - 114 + 113 src/app/components/admin/users-groups/users-groups.component.ts @@ -2748,15 +2777,15 @@ src/app/components/manage/document-attributes/management-list/management-list.component.ts - 455 + 454 src/app/components/manage/mail/mail.component.ts - 201 + 195 src/app/components/manage/mail/mail.component.ts - 302 + 297 src/app/components/manage/workflows/workflows.component.ts @@ -2767,70 +2796,70 @@ Document "" deleted src/app/components/admin/trash/trash.component.ts - 90 + 89 Error deleting document "" src/app/components/admin/trash/trash.component.ts - 97 + 96 This operation will permanently delete the selected documents. src/app/components/admin/trash/trash.component.ts - 112 + 111 This operation will permanently delete all documents in the trash. src/app/components/admin/trash/trash.component.ts - 113 + 112 Document(s) deleted src/app/components/admin/trash/trash.component.ts - 124 + 123 Error deleting document(s) src/app/components/admin/trash/trash.component.ts - 131 + 130 Document "" restored src/app/components/admin/trash/trash.component.ts - 144 + 143 Error restoring document "" src/app/components/admin/trash/trash.component.ts - 155 + 154 Document(s) restored src/app/components/admin/trash/trash.component.ts - 167 + 166 Error restoring document(s) src/app/components/admin/trash/trash.component.ts - 173 + 172 @@ -3008,7 +3037,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 201 + 212 @@ -3051,23 +3080,23 @@ src/app/components/document-detail/document-detail.component.ts - 1381 + 1405 src/app/components/document-detail/document-detail.component.ts - 1763 + 1788 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 899 + 908 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 935 + 944 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 958 + 967 src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts @@ -3075,15 +3104,15 @@ src/app/components/manage/document-attributes/management-list/management-list.component.ts - 457 + 456 src/app/components/manage/mail/mail.component.ts - 203 + 197 src/app/components/manage/mail/mail.component.ts - 304 + 299 src/app/components/manage/workflows/workflows.component.ts @@ -3252,7 +3281,7 @@ src/app/components/manage/document-attributes/document-attributes.component.ts - 99 + 95 @@ -3263,7 +3292,7 @@ src/app/components/manage/document-attributes/document-attributes.component.ts - 109 + 105 @@ -3274,7 +3303,7 @@ src/app/components/manage/document-attributes/document-attributes.component.ts - 119 + 115 @@ -3293,7 +3322,7 @@ src/app/components/manage/document-attributes/document-attributes.component.ts - 129 + 125 @@ -3386,21 +3415,21 @@ Sidebar views updated src/app/components/app-frame/app-frame.component.ts - 383 + 396 Error updating sidebar views src/app/components/app-frame/app-frame.component.ts - 386 + 399 An error occurred while saving update checking settings. src/app/components/app-frame/app-frame.component.ts - 407 + 420 @@ -3429,7 +3458,7 @@ src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 104 + 120 src/app/components/document-detail/document-detail.component.html @@ -3543,22 +3572,22 @@ Successfully updated object. src/app/components/app-frame/global-search/global-search.component.ts - 211 + 213 src/app/components/app-frame/global-search/global-search.component.ts - 249 + 251 Error occurred saving object. src/app/components/app-frame/global-search/global-search.component.ts - 214 + 216 src/app/components/app-frame/global-search/global-search.component.ts - 252 + 254 @@ -3626,31 +3655,31 @@ src/app/components/document-detail/document-detail.component.ts - 1334 + 1358 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 536 + 545 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 576 + 585 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 614 + 623 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 652 + 661 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 714 + 723 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 847 + 856 @@ -3664,42 +3693,42 @@ {VAR_PLURAL, plural, =1 {One page} other { pages}} src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 25 + 28 Use metadata from: src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 34 + 38 Regenerate all metadata src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 36 + 40 Try to include archive version in merge for non-PDF files src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 44 + 48 Delete original documents after successful merge src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 48 + 52 Note that only PDFs will be included. src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html - 51 + 55 @@ -3734,33 +3763,33 @@ Remove password protection src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts - 18 + 19 src/app/components/document-detail/document-detail.component.ts - 1816 + 1841 Create an unprotected copy or replace the existing file. src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts - 22 + 20 src/app/components/document-detail/document-detail.component.ts - 1817 + 1842 Start src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts - 25 + 21 src/app/components/document-detail/document-detail.component.ts - 1818 + 1843 @@ -3814,7 +3843,7 @@ Saved field "". src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts - 130 + 129 src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts @@ -3825,7 +3854,7 @@ Error saving field. src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.ts - 139 + 138 src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts @@ -3910,11 +3939,11 @@ src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 94 + 96 src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 100 + 102 @@ -3925,29 +3954,29 @@ src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 95 + 97 src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 101 + 103 Search docs... src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 69 + 70 src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 117 + 119 Any src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 149 + 152 src/app/components/common/filterable-dropdown/filterable-dropdown.component.html @@ -3958,21 +3987,21 @@ Not src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 154 + 157 Add query src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 173 + 176 Add expression src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html - 176 + 179 @@ -4016,7 +4045,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 90 + 91 src/app/components/document-list/document-list.component.html @@ -4641,23 +4670,23 @@ src/app/components/common/system-status-dialog/system-status-dialog.component.html - 197 + 199 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 231 + 233 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 265 + 267 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 275 + 277 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 313 + 315 src/app/components/common/toast/toast.component.html @@ -4672,158 +4701,158 @@ Only process attachments src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 38 + 39 src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 49 + 50 Process all files, including 'inline' attachments src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 42 + 43 Process message as .eml src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 53 + 54 Process message as .eml and attachments separately src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 57 + 58 System default src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 64 + 65 Text, then HTML src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 68 + 69 HTML, then text src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 72 + 73 HTML only src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 76 + 77 Text only src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 80 + 81 Move to specified folder src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 91 + 92 Mark as read, don't process read mails src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 95 + 96 Flag the mail, don't process flagged mails src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 99 + 100 Tag the mail with specified tag, don't process tagged mails src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 103 + 104 Use subject as title src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 110 + 111 Use attachment filename as title src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 114 + 115 Do not assign title from this rule src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 118 + 119 Do not assign a correspondent src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 125 + 126 Use mail address src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 129 + 130 Use name (or mail address if not available) src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 133 + 134 Use correspondent selected below src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 137 + 138 Create new mail rule src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 191 + 183 Edit mail rule src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts - 195 + 187 @@ -4955,7 +4984,7 @@ src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 136 + 137 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -5065,32 +5094,32 @@ Create new user account src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 70 + 68 Edit user account src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 74 + 72 Totp deactivated src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 130 + 129 Totp deactivation failed src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 133 + 132 src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 138 + 137 @@ -5584,14 +5613,14 @@ Consume Folder src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 71 + 72 Modified src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 98 + 99 src/app/data/document.ts @@ -5602,176 +5631,176 @@ Custom Field src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 102 + 103 Consumption Started src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 109 + 110 Document Added src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 113 + 114 Document Updated src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 117 + 118 Assignment src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 128 + 129 Removal src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 132 + 133 Webhook src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 140 + 141 Password removal src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 144 + 145 Move to trash src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 148 + 149 src/app/components/document-detail/document-detail.component.ts - 1338 + 1362 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 851 + 860 Has any of these tags src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 217 + 218 Has all of these tags src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 224 + 225 Does not have these tags src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 231 + 232 Has any of these correspondents src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 238 + 239 Has correspondent src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 246 + 247 Does not have correspondents src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 254 + 255 Has document type src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 262 + 263 Has any of these document types src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 270 + 271 Does not have document types src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 278 + 279 Has storage path src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 286 + 287 Has any of these storage paths src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 294 + 295 Does not have storage paths src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 302 + 303 Matches custom field query src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 310 + 311 Create new workflow src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 539 + 522 Edit workflow src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts - 543 + 526 @@ -5831,21 +5860,21 @@ Email sent src/app/components/common/email-document-dialog/email-document-dialog.component.ts - 63 + 53 Error emailing documents src/app/components/common/email-document-dialog/email-document-dialog.component.ts - 69 + 59 Error emailing document src/app/components/common/email-document-dialog/email-document-dialog.component.ts - 70 + 60 @@ -5910,7 +5939,7 @@ Keyboard shortcuts src/app/components/common/hotkey-dialog/hotkey-dialog.component.ts - 24 + 23 @@ -5979,11 +6008,11 @@ src/app/components/common/input/select/select.component.html - 61 + 62 src/app/components/common/input/tags/tags.component.html - 65 + 66 @@ -5994,7 +6023,7 @@ src/app/components/common/input/select/select.component.ts - 172 + 176 @@ -6098,7 +6127,7 @@ Private src/app/components/common/input/select/select.component.ts - 71 + 72 src/app/components/common/tag/tag.component.html @@ -6121,7 +6150,7 @@ No items found src/app/components/common/input/select/select.component.ts - 106 + 110 @@ -6135,21 +6164,21 @@ Add tag src/app/components/common/input/tags/tags.component.html - 17 + 18 Remove tag src/app/components/common/input/tags/tags.component.html - 23 + 24 Filter documents with these Tags src/app/components/common/input/tags/tags.component.html - 55 + 56 @@ -6288,28 +6317,28 @@ Set permissions src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 41 + 43 Edit permissions for src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 49 + 54 Existing owner, user and group permissions will be merged with these settings. src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 90 + 93 Any and all existing owner, user and group permissions will be replaced. src/app/components/common/permissions-dialog/permissions-dialog.component.ts - 91 + 94 @@ -6390,7 +6419,7 @@ Open preview src/app/components/common/preview-popup/preview-popup.component.ts - 54 + 61 @@ -6441,7 +6470,7 @@ src/app/components/common/system-status-dialog/system-status-dialog.component.html - 332 + 334 src/app/components/manage/mail/mail.component.html @@ -6562,85 +6591,85 @@ Emails must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 148 + 146 Passwords must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 176 + 173 Profile updated successfully src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 198 + 209 Error saving profile src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 212 + 223 Error generating auth token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 230 + 241 Error disconnecting social account src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 255 + 266 Error fetching TOTP settings src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 274 + 285 TOTP activated successfully src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 295 + 306 Error activating TOTP src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 297 + 308 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 303 + 314 TOTP deactivated successfully src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 319 + 330 Error deactivating TOTP src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 321 + 332 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 326 + 337 @@ -6651,7 +6680,7 @@ - + more… + + more… src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html 22 @@ -6826,7 +6855,7 @@ src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 112 + 111 @@ -6900,42 +6929,42 @@ Failed to load share link bundles. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 66 + 65 Error retrieving share link bundles. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 68 + 67 Share link bundle deleted. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 121 + 120 Error deleting share link bundle. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 127 + 126 Share link bundle rebuild requested. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 139 + 138 Error requesting rebuild. src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.ts - 144 + 143 @@ -6963,7 +6992,7 @@ Share Links src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 31 + 30 src/app/components/document-detail/document-detail.component.html @@ -6974,14 +7003,14 @@ Error retrieving links src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 82 + 67 1 day src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 101 + 86 src/app/data/share-link.ts @@ -6992,21 +7021,21 @@ days src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 101 + 86 Error deleting link src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 130 + 115 Error creating link src/app/components/common/share-links-dialog/share-links-dialog.component.ts - 158 + 143 @@ -7147,31 +7176,31 @@ - Recent Task Activity ( days) + Recent Task Activity ( days) src/app/components/common/system-status-dialog/system-status-dialog.component.html - 145 + 147 Total src/app/components/common/system-status-dialog/system-status-dialog.component.html - 150 + 152 Successful src/app/components/common/system-status-dialog/system-status-dialog.component.html - 154 + 156 Failed src/app/components/common/system-status-dialog/system-status-dialog.component.html - 158 + 160 src/app/data/share-link-bundle.ts @@ -7182,7 +7211,7 @@ Pending src/app/components/common/system-status-dialog/system-status-dialog.component.html - 162 + 164 src/app/data/share-link-bundle.ts @@ -7193,96 +7222,96 @@ No recent tasks src/app/components/common/system-status-dialog/system-status-dialog.component.html - 167 + 169 Health src/app/components/common/system-status-dialog/system-status-dialog.component.html - 178 + 180 Search Index src/app/components/common/system-status-dialog/system-status-dialog.component.html - 182 + 184 Last Updated src/app/components/common/system-status-dialog/system-status-dialog.component.html - 195 + 197 Classifier src/app/components/common/system-status-dialog/system-status-dialog.component.html - 200 + 202 Run Task src/app/components/common/system-status-dialog/system-status-dialog.component.html - 222 + 224 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 256 + 258 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 304 + 306 Last Trained src/app/components/common/system-status-dialog/system-status-dialog.component.html - 229 + 231 Sanity Checker src/app/components/common/system-status-dialog/system-status-dialog.component.html - 234 + 236 Last Run src/app/components/common/system-status-dialog/system-status-dialog.component.html - 263 + 265 src/app/components/common/system-status-dialog/system-status-dialog.component.html - 311 + 313 WebSocket Connection src/app/components/common/system-status-dialog/system-status-dialog.component.html - 268 + 270 OK src/app/components/common/system-status-dialog/system-status-dialog.component.html - 272 + 274 AI Index src/app/components/common/system-status-dialog/system-status-dialog.component.html - 281 + 283 @@ -7317,28 +7346,28 @@ Dashboard updated src/app/components/dashboard/dashboard.component.ts - 94 + 92 Error updating dashboard src/app/components/dashboard/dashboard.component.ts - 97 + 95 Show all src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 10 + 13 Filter by correspondent src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 54 + 70 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -7353,7 +7382,7 @@ Filter by document type src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 64 + 80 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -7368,7 +7397,7 @@ Filter by storage path src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 69 + 85 src/app/components/document-list/document-card-large/document-card-large.component.html @@ -7383,14 +7412,14 @@ Filter by owner src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 74 + 90 Yes src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 84 + 100 src/app/components/document-list/document-list.component.html @@ -7401,7 +7430,7 @@ No src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 84 + 100 src/app/components/document-list/document-list.component.html @@ -7412,7 +7441,7 @@ No documents src/app/components/dashboard/widgets/saved-view-widget/saved-view-widget.component.html - 149 + 184 @@ -7475,7 +7504,7 @@ Other src/app/components/dashboard/widgets/statistics-widget/statistics-widget.component.ts - 79 + 78 @@ -7529,7 +7558,7 @@ src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 481 + 490 this string is used to separate processing, failed and added on the file upload widget @@ -7591,7 +7620,7 @@ - of + of src/app/components/document-detail/document-detail.component.html 7,8 @@ -7838,7 +7867,7 @@ - Notes + Notes src/app/components/document-detail/document-detail.component.html 368,371 @@ -7852,7 +7881,7 @@ - Duplicates + Duplicates src/app/components/document-detail/document-detail.component.html 401,405 @@ -7883,304 +7912,304 @@ Save & close src/app/components/document-detail/document-detail.component.html - 450 + 449 Discard src/app/components/document-detail/document-detail.component.html - 452 + 451 Document loading... src/app/components/document-detail/document-detail.component.html - 460 + 459 Enter Password src/app/components/document-detail/document-detail.component.html - 512 + 516 Error retrieving metadata src/app/components/document-detail/document-detail.component.ts - 411 + 423 An error occurred loading content: src/app/components/document-detail/document-detail.component.ts - 512,514 + 525,527 src/app/components/document-detail/document-detail.component.ts - 959,961 + 982,984 Document was updated src/app/components/document-detail/document-detail.component.ts - 632 + 648 Document was updated at . src/app/components/document-detail/document-detail.component.ts - 633 + 649 Reload to discard your local unsaved edits and load the latest remote version. src/app/components/document-detail/document-detail.component.ts - 634 + 650 Reload src/app/components/document-detail/document-detail.component.ts - 636 + 652 Document reloaded with latest changes. src/app/components/document-detail/document-detail.component.ts - 692 + 708 Document reloaded. src/app/components/document-detail/document-detail.component.ts - 703 + 719 Next document src/app/components/document-detail/document-detail.component.ts - 805 + 821 Previous document src/app/components/document-detail/document-detail.component.ts - 815 + 831 Close document src/app/components/document-detail/document-detail.component.ts - 823 + 839 src/app/services/open-documents.service.ts - 130 + 143 Save document src/app/components/document-detail/document-detail.component.ts - 830 + 846 Save and close / next src/app/components/document-detail/document-detail.component.ts - 839 + 855 Error retrieving version content src/app/components/document-detail/document-detail.component.ts - 943 + 965 Error retrieving suggestions. src/app/components/document-detail/document-detail.component.ts - 1002 + 1026 Document "" saved successfully. src/app/components/document-detail/document-detail.component.ts - 1214 + 1238 src/app/components/document-detail/document-detail.component.ts - 1241 + 1265 - Error saving document "" + Error saving document "" src/app/components/document-detail/document-detail.component.ts - 1247 + 1271 Error saving document src/app/components/document-detail/document-detail.component.ts - 1302 + 1326 - Do you really want to move the document "" to the trash? + Do you really want to move the document "" to the trash? src/app/components/document-detail/document-detail.component.ts - 1335 + 1359 Documents can be restored prior to permanent deletion. src/app/components/document-detail/document-detail.component.ts - 1336 + 1360 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 849 + 858 Error deleting document src/app/components/document-detail/document-detail.component.ts - 1357 + 1381 Reprocess confirm src/app/components/document-detail/document-detail.component.ts - 1377 + 1401 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 895 + 904 This operation will permanently recreate the archive file for this document. src/app/components/document-detail/document-detail.component.ts - 1378 + 1402 The archive file will be re-generated with the current settings. src/app/components/document-detail/document-detail.component.ts - 1379 + 1403 - Reprocess operation for "" will begin in the background. + Reprocess operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1389 + 1413 Error executing operation src/app/components/document-detail/document-detail.component.ts - 1400 + 1424 Error downloading document src/app/components/document-detail/document-detail.component.ts - 1463 + 1487 Page Fit src/app/components/document-detail/document-detail.component.ts - 1543 + 1565 - PDF edit operation for "" will begin in the background. + PDF edit operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1783 + 1808 Error executing PDF edit operation src/app/components/document-detail/document-detail.component.ts - 1795 + 1820 Please enter the current password before attempting to remove it. src/app/components/document-detail/document-detail.component.ts - 1806 + 1831 - Password removal operation for "" will begin in the background. + Password removal operation for "" will begin in the background. src/app/components/document-detail/document-detail.component.ts - 1840 + 1865 Error executing password removal operation src/app/components/document-detail/document-detail.component.ts - 1854 + 1879 Print failed. src/app/components/document-detail/document-detail.component.ts - 1900 + 1925 Error loading document for printing. src/app/components/document-detail/document-detail.component.ts - 1913 + 1938 An error occurred loading tiff: src/app/components/document-detail/document-detail.component.ts - 1978 + 2008 src/app/components/document-detail/document-detail.component.ts - 1982 + 2014 @@ -8431,18 +8460,18 @@ "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 473 + 482 src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 479 + 488 "" and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 475 + 484 This is for messages like 'modify "tag1" and "tag2"' @@ -8450,7 +8479,7 @@ and "" src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 483,485 + 492,494 this is for messages like 'modify "tag1", "tag2" and "tag3"' @@ -8458,14 +8487,14 @@ Confirm tags assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 500 + 509 This operation will add the tag "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 506 + 515 @@ -8474,14 +8503,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 511,513 + 520,522 This operation will remove the tag "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 519 + 528 @@ -8490,7 +8519,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 524,526 + 533,535 @@ -8501,84 +8530,84 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 528,532 + 537,541 Confirm correspondent assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 569 + 578 This operation will assign the correspondent "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 571 + 580 This operation will remove the correspondent from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 573 + 582 Confirm document type assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 607 + 616 This operation will assign the document type "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 609 + 618 This operation will remove the document type from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 611 + 620 Confirm storage path assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 645 + 654 This operation will assign the storage path "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 647 + 656 This operation will remove the storage path from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 649 + 658 Confirm custom field assignment src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 678 + 687 This operation will assign the custom field "" to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 684 + 693 @@ -8587,14 +8616,14 @@ )"/> to selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 689,691 + 698,700 This operation will remove the custom field "" from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 697 + 706 @@ -8603,7 +8632,7 @@ )"/> from selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 702,704 + 711,713 @@ -8614,91 +8643,91 @@ )"/> on selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 706,710 + 715,719 Move selected document(s) to the trash? src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 848 + 857 This operation will permanently recreate the archive files for selected document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 896 + 905 The archive files will be re-generated with the current settings. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 897 + 906 Rotate confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 932 + 941 This operation will add rotated versions of the document(s). src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 933 + 942 Merge confirm src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 956 + 965 This operation will merge selected documents into a new document. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 957 + 966 Merged document will be queued for consumption. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 980 + 989 Custom fields updated. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 1004 + 1013 Error updating custom fields. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 1013 + 1022 Share link bundle creation requested. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 1053 + 1062 Share link bundle creation is not available yet. src/app/components/document-list/bulk-editor/bulk-editor.component.ts - 1060 + 1069 @@ -8742,7 +8771,7 @@ - Created: + Created: src/app/components/document-list/document-card-large/document-card-large.component.html 115,116 @@ -8757,7 +8786,7 @@ - Added: + Added: src/app/components/document-list/document-card-large/document-card-large.component.html 116,117 @@ -8772,7 +8801,7 @@ - Modified: + Modified: src/app/components/document-list/document-card-large/document-card-large.component.html 117,118 @@ -9197,14 +9226,14 @@ View "" created successfully. src/app/components/document-list/document-list.component.ts - 488 + 490 View "" created successfully, but could not update visibility settings. src/app/components/document-list/document-list.component.ts - 494 + 496 @@ -9467,14 +9496,14 @@ Error saving note src/app/components/document-notes/document-notes.component.ts - 81 + 83 Error deleting note src/app/components/document-notes/document-notes.component.ts - 95 + 97 @@ -9759,92 +9788,92 @@ Successfully created . src/app/components/manage/document-attributes/management-list/management-list.component.ts - 229 + 228 Error occurred while creating . src/app/components/manage/document-attributes/management-list/management-list.component.ts - 234 + 233 Successfully updated "". src/app/components/manage/document-attributes/management-list/management-list.component.ts - 249 + 248 Error occurred while saving . src/app/components/manage/document-attributes/management-list/management-list.component.ts - 254 + 253 Associated documents will not be deleted. src/app/components/manage/document-attributes/management-list/management-list.component.ts - 274 + 273 Error while deleting element src/app/components/manage/document-attributes/management-list/management-list.component.ts - 290 + 289 Error saving settings src/app/components/manage/document-attributes/management-list/management-list.component.ts - 329 + 328 Permissions updated successfully src/app/components/manage/document-attributes/management-list/management-list.component.ts - 433 + 432 Error updating permissions src/app/components/manage/document-attributes/management-list/management-list.component.ts - 440 + 439 src/app/components/manage/mail/mail.component.ts - 351 + 346 src/app/components/manage/saved-views/saved-views.component.ts - 274 + 278 This operation will permanently delete the selected . src/app/components/manage/document-attributes/management-list/management-list.component.ts - 454 + 453 Objects deleted successfully src/app/components/manage/document-attributes/management-list/management-list.component.ts - 472 + 471 Error deleting objects src/app/components/manage/document-attributes/management-list/management-list.component.ts - 478 + 477 @@ -9999,162 +10028,162 @@ Error retrieving mail accounts src/app/components/manage/mail/mail.component.ts - 114 + 110 Error retrieving mail rules src/app/components/manage/mail/mail.component.ts - 136 + 130 OAuth2 authentication success src/app/components/manage/mail/mail.component.ts - 144 + 138 OAuth2 authentication failed, see logs for details src/app/components/manage/mail/mail.component.ts - 155 + 149 Saved account "". src/app/components/manage/mail/mail.component.ts - 179 + 173 Error saving account. src/app/components/manage/mail/mail.component.ts - 191 + 185 Confirm delete mail account src/app/components/manage/mail/mail.component.ts - 199 + 193 This operation will permanently delete this mail account. src/app/components/manage/mail/mail.component.ts - 200 + 194 Deleted mail account "" src/app/components/manage/mail/mail.component.ts - 210 + 204 Error deleting mail account "". src/app/components/manage/mail/mail.component.ts - 221 + 215 Processing mail account "" src/app/components/manage/mail/mail.component.ts - 233 + 227 Error processing mail account "" src/app/components/manage/mail/mail.component.ts - 238 + 232 Saved rule "". src/app/components/manage/mail/mail.component.ts - 256 + 251 Error saving rule. src/app/components/manage/mail/mail.component.ts - 267 + 262 Rule "" enabled. src/app/components/manage/mail/mail.component.ts - 283 + 278 Rule "" disabled. src/app/components/manage/mail/mail.component.ts - 284 + 279 Error toggling rule "". src/app/components/manage/mail/mail.component.ts - 289 + 284 Confirm delete mail rule src/app/components/manage/mail/mail.component.ts - 300 + 295 This operation will permanently delete this mail rule. src/app/components/manage/mail/mail.component.ts - 301 + 296 Deleted mail rule "" src/app/components/manage/mail/mail.component.ts - 311 + 306 Error deleting mail rule "". src/app/components/manage/mail/mail.component.ts - 322 + 317 Permissions updated src/app/components/manage/mail/mail.component.ts - 346 + 341 src/app/components/manage/saved-views/saved-views.component.ts - 268 + 272 - Processed Mail for + Processed Mail for src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.html 2 @@ -10185,7 +10214,7 @@ Processed mail(s) deleted src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.ts - 72 + 70 @@ -10255,28 +10284,28 @@ Saved view "" deleted. src/app/components/manage/saved-views/saved-views.component.ts - 153 + 155 Views saved successfully. src/app/components/manage/saved-views/saved-views.component.ts - 230 + 232 Error while saving views. src/app/components/manage/saved-views/saved-views.component.ts - 235 + 237 Note: Sharing saved views does not share the underlying documents. src/app/components/manage/saved-views/saved-views.component.ts - 257 + 260 @@ -10910,6 +10939,20 @@ 361 + + LLM Request Timeout + + src/app/data/paperless-config.ts + 365 + + + + Timeout in seconds for LLM requests. + + src/app/data/paperless-config.ts + 369 + + Processing @@ -10971,11 +11014,11 @@ src/app/services/open-documents.service.ts - 122 + 135 src/app/services/open-documents.service.ts - 149 + 162 @@ -10986,7 +11029,7 @@ src/app/services/open-documents.service.ts - 150 + 163 @@ -11126,315 +11169,315 @@ You have unsaved changes to the document src/app/services/open-documents.service.ts - 124 + 137 Are you sure you want to close this document? src/app/services/open-documents.service.ts - 128 + 141 Are you sure you want to close all documents? src/app/services/open-documents.service.ts - 151 + 164 Close documents src/app/services/open-documents.service.ts - 153 + 166 English (US) src/app/services/settings.service.ts - 51 + 52 Afrikaans src/app/services/settings.service.ts - 57 + 58 Arabic src/app/services/settings.service.ts - 63 + 64 Belarusian src/app/services/settings.service.ts - 69 + 70 Bulgarian src/app/services/settings.service.ts - 75 + 76 Catalan src/app/services/settings.service.ts - 81 + 82 Czech src/app/services/settings.service.ts - 87 + 88 Danish src/app/services/settings.service.ts - 93 + 94 German src/app/services/settings.service.ts - 99 + 100 Greek src/app/services/settings.service.ts - 105 + 106 English (GB) src/app/services/settings.service.ts - 111 + 112 Spanish src/app/services/settings.service.ts - 117 + 118 Finnish src/app/services/settings.service.ts - 123 + 124 French src/app/services/settings.service.ts - 129 + 130 Hungarian src/app/services/settings.service.ts - 135 + 136 Indonesian src/app/services/settings.service.ts - 141 + 142 Italian src/app/services/settings.service.ts - 147 + 148 Japanese src/app/services/settings.service.ts - 153 + 154 Korean src/app/services/settings.service.ts - 159 + 160 Luxembourgish src/app/services/settings.service.ts - 165 + 166 Dutch src/app/services/settings.service.ts - 171 + 172 Norwegian src/app/services/settings.service.ts - 177 + 178 Persian src/app/services/settings.service.ts - 183 + 184 Polish src/app/services/settings.service.ts - 189 + 190 Portuguese (Brazil) src/app/services/settings.service.ts - 195 + 196 Portuguese src/app/services/settings.service.ts - 201 + 202 Romanian src/app/services/settings.service.ts - 207 + 208 Russian src/app/services/settings.service.ts - 213 + 214 Slovak src/app/services/settings.service.ts - 219 + 220 Slovenian src/app/services/settings.service.ts - 225 + 226 Serbian src/app/services/settings.service.ts - 231 + 232 Swedish src/app/services/settings.service.ts - 237 + 238 Turkish src/app/services/settings.service.ts - 243 + 244 Ukrainian src/app/services/settings.service.ts - 249 + 250 Vietnamese src/app/services/settings.service.ts - 255 + 256 Chinese Simplified src/app/services/settings.service.ts - 261 + 262 Chinese Traditional src/app/services/settings.service.ts - 267 + 268 ISO 8601 src/app/services/settings.service.ts - 275 + 276 Successfully completed one-time migratration of settings to the database! src/app/services/settings.service.ts - 635 + 636 Unable to migrate settings to the database, please try saving manually. src/app/services/settings.service.ts - 636 + 637 You can restart the tour from the settings page. src/app/services/settings.service.ts - 708 + 709 @@ -11448,21 +11491,21 @@ Uploading... src/app/services/upload-documents.service.ts - 37 + 38 Upload complete, waiting... src/app/services/upload-documents.service.ts - 40 + 42 HTTP error: src/app/services/upload-documents.service.ts - 53 + 56 diff --git a/src-ui/package.json b/src-ui/package.json index b0ad88fc3..2a7bdfe11 100644 --- a/src-ui/package.json +++ b/src-ui/package.json @@ -6,22 +6,22 @@ "ng": "ng", "start": "ng serve", "build": "ng build", - "test": "ng test --no-watch --coverage", + "test": "ng test", "lint": "ng lint" }, "private": true, "dependencies": { - "@angular/cdk": "^21.2.12", - "@angular/common": "~21.2.17", - "@angular/compiler": "~21.2.17", - "@angular/core": "~21.2.17", - "@angular/forms": "~21.2.14", - "@angular/localize": "~21.2.14", - "@angular/platform-browser": "~21.2.14", - "@angular/platform-browser-dynamic": "~21.2.14", - "@angular/router": "~21.2.14", - "@ng-bootstrap/ng-bootstrap": "^20.0.0", - "@ng-select/ng-select": "^21.8.2", + "@angular/cdk": "^22.0.3", + "@angular/common": "~22.0.5", + "@angular/compiler": "~22.0.5", + "@angular/core": "~22.0.5", + "@angular/forms": "~22.0.5", + "@angular/localize": "~22.0.5", + "@angular/platform-browser": "~22.0.5", + "@angular/platform-browser-dynamic": "~22.0.5", + "@angular/router": "~22.0.5", + "@ng-bootstrap/ng-bootstrap": "^21.0.0", + "@ng-select/ng-select": "^23.2.0", "@ngneat/dirty-check-forms": "^3.0.3", "@popperjs/core": "^2.11.8", "bootstrap": "^5.3.8", @@ -29,56 +29,46 @@ "mime-names": "^1.0.0", "ngx-bootstrap-icons": "^1.9.3", "ngx-color": "^10.1.0", - "ngx-cookie-service": "^21.3.1", - "ngx-device-detector": "^11.0.0", - "ngx-ui-tour-ng-bootstrap": "^18.0.0", + "ngx-cookie-service": "^22.0.0", + "ngx-device-detector": "^12.0.0", + "ngx-ui-tour-ng-bootstrap": "^19.0.0", "normalize-diacritics": "^5.0.0", - "pdfjs-dist": "^5.7.284", + "pdfjs-dist": "^6.0.227", "rxjs": "^7.8.2", "tslib": "^2.8.1", "utif": "^3.1.0", - "uuid": "^14.0.0", - "zone.js": "^0.16.2" + "uuid": "^14.0.1" }, "devDependencies": { - "@angular-builders/custom-webpack": "^21.0.3", - "@angular-builders/jest": "^21.0.3", - "@angular-devkit/core": "^21.2.12", - "@angular-devkit/schematics": "^21.2.12", - "@angular-eslint/builder": "21.4.0", - "@angular-eslint/eslint-plugin": "21.4.0", - "@angular-eslint/eslint-plugin-template": "21.4.0", - "@angular-eslint/schematics": "21.4.0", - "@angular-eslint/template-parser": "21.4.0", - "@angular/build": "^21.2.12", - "@angular/cli": "~21.2.12", - "@angular/compiler-cli": "~21.2.14", + "@angular-builders/custom-webpack": "^22.0.1", + "@angular-builders/jest": "^22.0.1", + "@angular-devkit/core": "^22.0.5", + "@angular-devkit/schematics": "^22.0.5", + "@angular-eslint/builder": "22.0.0", + "@angular-eslint/eslint-plugin": "22.0.0", + "@angular-eslint/eslint-plugin-template": "22.0.0", + "@angular-eslint/schematics": "22.0.0", + "@angular-eslint/template-parser": "22.0.0", + "@angular/build": "^22.0.5", + "@angular/cli": "~22.0.5", + "@angular/compiler-cli": "~22.0.5", "@codecov/webpack-plugin": "^2.0.1", - "@playwright/test": "^1.60.0", + "@playwright/test": "^1.61.1", "@types/jest": "^30.0.0", - "@types/node": "^25.9.1", - "@typescript-eslint/eslint-plugin": "^8.60.0", - "@typescript-eslint/parser": "^8.60.0", - "@typescript-eslint/utils": "^8.60.0", - "eslint": "^10.4.0", + "@types/node": "^26.0.0", + "@typescript-eslint/eslint-plugin": "^8.62.0", + "@typescript-eslint/parser": "^8.62.0", + "@typescript-eslint/utils": "^8.62.0", + "eslint": "^10.5.0", "jest": "30.4.2", "jest-environment-jsdom": "^30.4.1", "jest-junit": "^17.0.0", - "jest-preset-angular": "^16.1.5", + "jest-preset-angular": "^17.0.0", "jest-websocket-mock": "^2.5.0", "prettier-plugin-organize-imports": "^4.3.0", "ts-node": "~10.9.1", - "typescript": "^5.9.3", + "typescript": "^6.0.3", "webpack": "^5.107.2" }, - "packageManager": "pnpm@10.17.1", - "pnpm": { - "onlyBuiltDependencies": [ - "@parcel/watcher", - "canvas", - "esbuild", - "lmdb", - "msgpackr-extract" - ] - } + "packageManager": "pnpm@10.26.0" } diff --git a/src-ui/pnpm-lock.yaml b/src-ui/pnpm-lock.yaml index caf7d1c60..cd3076cb0 100644 --- a/src-ui/pnpm-lock.yaml +++ b/src-ui/pnpm-lock.yaml @@ -9,41 +9,41 @@ importers: .: dependencies: '@angular/cdk': - specifier: ^21.2.12 - version: 21.2.12(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: ^22.0.3 + version: 22.0.3(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) '@angular/common': - specifier: ~21.2.17 - version: 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + specifier: ~22.0.5 + version: 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) '@angular/compiler': - specifier: ~21.2.17 - version: 21.2.17 + specifier: ~22.0.5 + version: 22.0.5 '@angular/core': - specifier: ~21.2.17 - version: 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + specifier: ~22.0.5 + version: 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) '@angular/forms': - specifier: ~21.2.14 - version: 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: ~22.0.5 + version: 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) '@angular/localize': - specifier: ~21.2.14 - version: 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) + specifier: ~22.0.5 + version: 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5) '@angular/platform-browser': - specifier: ~21.2.14 - version: 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: ~22.0.5 + version: 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) '@angular/platform-browser-dynamic': - specifier: ~21.2.14 - version: 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))) + specifier: ~22.0.5 + version: 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))) '@angular/router': - specifier: ~21.2.14 - version: 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + specifier: ~22.0.5 + version: 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) '@ng-bootstrap/ng-bootstrap': - specifier: ^20.0.0 - version: 20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2) + specifier: ^21.0.0 + version: 21.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@popperjs/core@2.11.8)(rxjs@7.8.2) '@ng-select/ng-select': - specifier: ^21.8.2 - version: 21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)) + specifier: ^23.2.0 + version: 23.2.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)) '@ngneat/dirty-check-forms': specifier: ^3.0.3 - version: 3.0.3(ad2c8ff51b8ef8626e139c84727a024d) + version: 3.0.3(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/router@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -58,25 +58,25 @@ importers: version: 1.0.0 ngx-bootstrap-icons: specifier: ^1.9.3 - version: 1.9.3(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 1.9.3(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) ngx-color: specifier: ^10.1.0 - version: 10.1.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + version: 10.1.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) ngx-cookie-service: - specifier: ^21.3.1 - version: 21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: ^22.0.0 + version: 22.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) ngx-device-detector: - specifier: ^11.0.0 - version: 11.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + specifier: ^12.0.0 + version: 12.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) ngx-ui-tour-ng-bootstrap: - specifier: ^18.0.0 - version: 18.0.0(4ccfccfbcf381a309618492b31e99276) + specifier: ^19.0.0 + version: 19.0.0(5d0f9d2d91ce22b3b7a50a70fd62ec66) normalize-diacritics: specifier: ^5.0.0 version: 5.0.0 pdfjs-dist: - specifier: ^5.7.284 - version: 5.7.284 + specifier: ^6.0.227 + version: 6.0.227 rxjs: specifier: ^7.8.2 version: 7.8.2 @@ -87,75 +87,72 @@ importers: specifier: ^3.1.0 version: 3.1.0 uuid: - specifier: ^14.0.0 - version: 14.0.0 - zone.js: - specifier: ^0.16.2 - version: 0.16.2 + specifier: ^14.0.1 + version: 14.0.1 devDependencies: '@angular-builders/custom-webpack': - specifier: ^21.0.3 - version: 21.0.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jest-environment-jsdom@30.4.1(canvas@3.0.0))(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(jiti@2.6.1)(less@4.4.2)(postcss@8.5.15)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0) + specifier: ^22.0.1 + version: 22.0.1(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.15)(rxjs@7.8.2)(terser@5.48.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0) '@angular-builders/jest': - specifier: ^21.0.3 - version: 21.0.3(6a682f4f002a31c8d3b52779d7b9ab9b) + specifier: ^22.0.1 + version: 22.0.1(41a81beaf45fc81545506474f268b72a) '@angular-devkit/core': - specifier: ^21.2.12 - version: 21.2.12(chokidar@5.0.0) + specifier: ^22.0.5 + version: 22.0.5(chokidar@5.0.0) '@angular-devkit/schematics': - specifier: ^21.2.12 - version: 21.2.12(chokidar@5.0.0) + specifier: ^22.0.5 + version: 22.0.5(chokidar@5.0.0) '@angular-eslint/builder': - specifier: 21.4.0 - version: 21.4.0(@angular/cli@21.2.12(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@angular/cli@22.0.5(@types/node@26.0.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@angular-eslint/eslint-plugin': - specifier: 21.4.0 - version: 21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@angular-eslint/eslint-plugin-template': - specifier: 21.4.0 - version: 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@angular-eslint/schematics': - specifier: 21.4.0 - version: 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(@angular/cli@21.2.12(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(chokidar@5.0.0)(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.0.0)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@angular-eslint/template-parser': - specifier: 21.4.0 - version: 21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: 22.0.0 + version: 22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@angular/build': - specifier: ^21.2.12 - version: 21.2.12(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.15)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0) + specifier: ^22.0.5 + version: 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.15)(terser@5.48.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0) '@angular/cli': - specifier: ~21.2.12 - version: 21.2.12(@types/node@25.9.1)(chokidar@5.0.0) + specifier: ~22.0.5 + version: 22.0.5(@types/node@26.0.0)(chokidar@5.0.0) '@angular/compiler-cli': - specifier: ~21.2.14 - version: 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) + specifier: ~22.0.5 + version: 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) '@codecov/webpack-plugin': specifier: ^2.0.1 version: 2.0.1(webpack@5.107.2(postcss@8.5.15)) '@playwright/test': - specifier: ^1.60.0 - version: 1.60.0 + specifier: ^1.61.1 + version: 1.61.1 '@types/jest': specifier: ^30.0.0 version: 30.0.0 '@types/node': - specifier: ^25.9.1 - version: 25.9.1 + specifier: ^26.0.0 + version: 26.0.0 '@typescript-eslint/eslint-plugin': - specifier: ^8.60.0 - version: 8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.62.0 + version: 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/parser': - specifier: ^8.60.0 - version: 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.62.0 + version: 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) '@typescript-eslint/utils': - specifier: ^8.60.0 - version: 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + specifier: ^8.62.0 + version: 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) eslint: - specifier: ^10.4.0 - version: 10.4.0(jiti@2.6.1) + specifier: ^10.5.0 + version: 10.5.0(jiti@2.7.0) jest: specifier: 30.4.2 - version: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + version: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) jest-environment-jsdom: specifier: ^30.4.1 version: 30.4.1(canvas@3.0.0) @@ -163,20 +160,20 @@ importers: specifier: ^17.0.0 version: 17.0.0 jest-preset-angular: - specifier: ^16.1.5 - version: 16.1.5(26662f94407112e0967a16d5ea795956) + specifier: ^17.0.0 + version: 17.0.0(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(canvas@3.0.0)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@6.0.3) jest-websocket-mock: specifier: ^2.5.0 version: 2.5.0 prettier-plugin-organize-imports: specifier: ^4.3.0 - version: 4.3.0(prettier@3.4.2)(typescript@5.9.3) + version: 4.3.0(prettier@3.4.2)(typescript@6.0.3) ts-node: specifier: ~10.9.1 - version: 10.9.2(@types/node@25.9.1)(typescript@5.9.3) + version: 10.9.2(@types/node@26.0.0)(typescript@6.0.3) typescript: - specifier: ^5.9.3 - version: 5.9.3 + specifier: ^6.0.3 + version: 6.0.3 webpack: specifier: ^5.107.2 version: 5.107.2(postcss@8.5.15) @@ -201,121 +198,110 @@ packages: '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} - '@algolia/abtesting@1.14.1': - resolution: {integrity: sha512-Dkj0BgPiLAaim9sbQ97UKDFHJE/880wgStAM18U++NaJ/2Cws34J5731ovJifr6E3Pv4T2CqvMXf8qLCC417Ew==} + '@algolia/abtesting@1.18.0': + resolution: {integrity: sha512-8siuLG+FIns1AjZ/g2SDVwHz9S+ObacDQISEJvS8XsNei1zl3FXqfqQrBpmrG7ACWCyesXHbicMJtvRbg00FEw==} engines: {node: '>= 14.0.0'} - '@algolia/client-abtesting@5.48.1': - resolution: {integrity: sha512-LV5qCJdj+/m9I+Aj91o+glYszrzd7CX6NgKaYdTOj4+tUYfbS62pwYgUfZprYNayhkQpVFcrW8x8ZlIHpS23Vw==} + '@algolia/client-abtesting@5.52.0': + resolution: {integrity: sha512-wtwPgyPmO7b7sQPVgoK29c1VpfS08DnnJCmxX/oU1pV2DlMRJCzQcLN7JSloYpodyKHwM8+9wOzlAM0co3TDmA==} engines: {node: '>= 14.0.0'} - '@algolia/client-analytics@5.48.1': - resolution: {integrity: sha512-/AVoMqHhPm14CcHq7mwB+bUJbfCv+jrxlNvRjXAuO+TQa+V37N8k1b0ijaRBPdmSjULMd8KtJbQyUyabXOu6Kg==} + '@algolia/client-analytics@5.52.0': + resolution: {integrity: sha512-9KY36bRl4AH7RjqSeDDOKnjsz4IxQFBEOB8/fWmEbdQe+Isbs5jGzVJu9NEPQ1Tgwxlf8Uf07Swj3jZyMNUZ2g==} engines: {node: '>= 14.0.0'} - '@algolia/client-common@5.48.1': - resolution: {integrity: sha512-VXO+qu2Ep6ota28ktvBm3sG53wUHS2n7bgLWmce5jTskdlCD0/JrV4tnBm1l7qpla1CeoQb8D7ShFhad+UoSOw==} + '@algolia/client-common@5.52.0': + resolution: {integrity: sha512-3a/qM3dzJqqfTx7Yrw7uGQ98I3Q0rDfb4Vkv0wEzko96l7YQMxfBVz/VbLq2N+c59GweYv6Vhp8mPeqnWJSITw==} engines: {node: '>= 14.0.0'} - '@algolia/client-insights@5.48.1': - resolution: {integrity: sha512-zl+Qyb0nLg+Y5YvKp1Ij+u9OaPaKg2/EPzTwKNiVyOHnQJlFxmXyUZL1EInczAZsEY8hVpPCLtNfhMhfxluXKQ==} + '@algolia/client-insights@5.52.0': + resolution: {integrity: sha512-Rki7ACbMcvbQW0BuM84x9dkGHY47ABmv4jU6tYssat2k02p3mIUms2YOLUAMeknhmnFsj6lb6ZzOXdMWMyc1sA==} engines: {node: '>= 14.0.0'} - '@algolia/client-personalization@5.48.1': - resolution: {integrity: sha512-r89Qf9Oo9mKWQXumRu/1LtvVJAmEDpn8mHZMc485pRfQUMAwSSrsnaw1tQ3sszqzEgAr1c7rw6fjBI+zrAXTOw==} + '@algolia/client-personalization@5.52.0': + resolution: {integrity: sha512-96s4Uzc3kk+/f4jJXIVVGWP5XlngOGNQ1x6hW9AT59pOixHlOs5tqJg+ZUS/GQ6h/iYP0ceQcmxDQeLyCLTaDQ==} engines: {node: '>= 14.0.0'} - '@algolia/client-query-suggestions@5.48.1': - resolution: {integrity: sha512-TPKNPKfghKG/bMSc7mQYD9HxHRUkBZA4q1PEmHgICaSeHQscGqL4wBrKkhfPlDV1uYBKW02pbFMUhsOt7p4ZpA==} + '@algolia/client-query-suggestions@5.52.0': + resolution: {integrity: sha512-lqeycNpSPe5Qa0OUWpejVvYQjQWV5nQuLT0a4aq7XzRAvCxprV/6Lf841EygdD2nrFnuS58ok7Au1uOtXzpnkg==} engines: {node: '>= 14.0.0'} - '@algolia/client-search@5.48.1': - resolution: {integrity: sha512-4Fu7dnzQyQmMFknYwTiN/HxPbH4DyxvQ1m+IxpPp5oslOgz8m6PG5qhiGbqJzH4HiT1I58ecDiCAC716UyVA8Q==} + '@algolia/client-search@5.52.0': + resolution: {integrity: sha512-ly1wETVGRo30cx61O7fetESN+ElL9c9K+bD/AVgnT1ar4c6v+/Yqjrhdtu6Fm4D0s4NZP081Isf6tunH1wUXHg==} engines: {node: '>= 14.0.0'} - '@algolia/ingestion@1.48.1': - resolution: {integrity: sha512-/RFq3TqtXDUUawwic/A9xylA2P3LDMO8dNhphHAUOU51b1ZLHrmZ6YYJm3df1APz7xLY1aht6okCQf+/vmrV9w==} + '@algolia/ingestion@1.52.0': + resolution: {integrity: sha512-U4EeTvgmluRjj39ykZSAd5X+a6LD5m7/mcOWDmB7hqm1R6QY0yT8jLxpNVEjYhzgEN5hcDGW6X67EWQY8KiYGQ==} engines: {node: '>= 14.0.0'} - '@algolia/monitoring@1.48.1': - resolution: {integrity: sha512-Of0jTeAZRyRhC7XzDSjJef0aBkgRcvRAaw0ooYRlOw57APii7lZdq+layuNdeL72BRq1snaJhoMMwkmLIpJScw==} + '@algolia/monitoring@1.52.0': + resolution: {integrity: sha512-FCPnDcILfpTE94u7BVlV4DmnSV5wE3+j25EEF+3dYPrVzkVCSoAHs318oWDGxnxsAgiL4HpL12Jc4XHmw9shpA==} engines: {node: '>= 14.0.0'} - '@algolia/recommend@5.48.1': - resolution: {integrity: sha512-bE7JcpFXzxF5zHwj/vkl2eiCBvyR1zQ7aoUdO+GDXxGp0DGw7nI0p8Xj6u8VmRQ+RDuPcICFQcCwRIJT5tDJFw==} + '@algolia/recommend@5.52.0': + resolution: {integrity: sha512-br3DO7n4N8CXwTRbZS0MnB4WQ9YHfNjCwkCEzVR/wek/qNTDQKDb0nROmkFaNZ8ucUqUVKZi074dbwMwRDlK8Q==} engines: {node: '>= 14.0.0'} - '@algolia/requester-browser-xhr@5.48.1': - resolution: {integrity: sha512-MK3wZ2koLDnvH/AmqIF1EKbJlhRS5j74OZGkLpxI4rYvNi9Jn/C7vb5DytBnQ4KUWts7QsmbdwHkxY5txQHXVw==} + '@algolia/requester-browser-xhr@5.52.0': + resolution: {integrity: sha512-b0T/Ca2c9KyEslKsVrGZvbe1UrrKKSdfXhBZ2pbpKahFUzJfziRZ0urbOm7V65O0tO/jwU+Lo/+bIiiyhzGt8w==} engines: {node: '>= 14.0.0'} - '@algolia/requester-fetch@5.48.1': - resolution: {integrity: sha512-2oDT43Y5HWRSIQMPQI4tA/W+TN/N2tjggZCUsqQV440kxzzoPGsvv9QP1GhQ4CoDa+yn6ygUsGp6Dr+a9sPPSg==} + '@algolia/requester-fetch@5.52.0': + resolution: {integrity: sha512-ozBT8J/mtD4H4IAojw8QPirlcL2gHrI1BGuZ4/ZXXO/rTE1yQ4VIPJj4mTTbwo4FbkS1MoJsD/DsrqLzhnc4/g==} engines: {node: '>= 14.0.0'} - '@algolia/requester-node-http@5.48.1': - resolution: {integrity: sha512-xcaCqbhupVWhuBP1nwbk1XNvwrGljozutEiLx06mvqDf3o8cHyEgQSHS4fKJM+UAggaWVnnFW+Nne5aQ8SUJXg==} + '@algolia/requester-node-http@5.52.0': + resolution: {integrity: sha512-gyyWcLD22tnabmoit4iukCXuoRc5HYJuUjPSEa8a0D/f/NlRafpWi52AlAaa4Uu/rsl7saHsJFTNjTptWbu2+A==} engines: {node: '>= 14.0.0'} '@ampproject/remapping@2.3.0': resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular-builders/common@5.0.3': - resolution: {integrity: sha512-Dro3574mu4/xqmjdA3159+TXDhgTbIJpEY/iBETSKUvHJiCgHel+R3eT105RpHN5o7NaD2rau5Zk2wuZqOk35Q==} + '@angular-builders/common@6.0.1': + resolution: {integrity: sha512-7qZDXSS3CmN4W7z/p+VwH38id4Tq++pjl0GqH6XVp7Jj7RZGH+d4vPfsqF/ePG4N1XEJjL9ieG0vP6MNtf9UMQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular-builders/custom-webpack@21.0.3': - resolution: {integrity: sha512-Aq3PZoQxY4jmfDb1sT5E6ZFAdiv35PziFmgYoW5DGX8xbYsV7EoKWZN2ZIjEI3Zr/WUZWGlxmxa11XK9WOtidg==} + '@angular-builders/custom-webpack@22.0.1': + resolution: {integrity: sha512-0wTSs9eH8LJjQYfuwySV9xOTxhfekbSaCFPHQv+GKsG0sAC/rjFG3sgRzObOd3ae3R8DW2389yZwu9juOWqRug==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler-cli': ^21.0.0 + '@angular/compiler-cli': ^22.0.0 + rxjs: '>=7.0.0' - '@angular-builders/jest@21.0.3': - resolution: {integrity: sha512-RYIsJQJkke4Dns+lYBYzn0JcmABCQKvTWkqMibi5v8dgtNS8pgPS8pE5x8DSmgraqJikL3ukqaUQmdeL6r38aw==} + '@angular-builders/jest@22.0.1': + resolution: {integrity: sha512-bSsV1a8A7KJs1RwsmKH5C9/K3j9GqRYQKiXPgrKOLUJ7dzsAvvlF58n5NX0Pn975ZCrGa1wY3tx8iwQpo+aj3g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular-devkit/build-angular': ^21.0.0 - '@angular/compiler-cli': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/platform-browser-dynamic': ^21.0.0 + '@angular-devkit/build-angular': ^22.0.0 + '@angular/compiler-cli': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/platform-browser-dynamic': ^22.0.0 jest: ^30.0.0 + rxjs: '>=7.0.0' - '@angular-devkit/architect@0.2101.2': - resolution: {integrity: sha512-pV2onJgp16xO0vAqEfRWVynRPPLVHydYLANNa3UX3l5T39JcYdMIoOHSIIl8tWrxVeOwiWd1ajub0VsFTUok4Q==} - 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.2200.5': + resolution: {integrity: sha512-Fbki7xEx37gn8uH5AFOh0EmfGV4y5xag2fTv1vn6KxY/h33L02KuAjiUX3h2YmSjnYSc1dXto3Q5c4lWs/I5Zw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular-devkit/architect@0.2102.12': - resolution: {integrity: sha512-w9FSMHYeeHkk0kRSAOCvNqEVyOHqpC1SUf3iN7tDnXBOA0dtc6JYvJU7O4joiwf7wMPZDK8LKc/6eu8/Tx87Fw==} - 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-devkit/architect@0.2102.13': - resolution: {integrity: sha512-fheyi0gPx6b7tT+WQ+ePlzdGqKjPLUK72wg5Z9pkVtQ5+VN/8yB9mlRlmoivngd2FeNG9wMeNynWZGYycnOWVw==} - 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-devkit/build-angular@21.1.2': - resolution: {integrity: sha512-i/FTbqVwj0Wk6B5RA2H9iVsDC/kIK/5koSEwkIQjXGZuDVFUoEuWiIR2PGGSSQ9u3DmkpVPZmKEXWRl+g7Qn5g==} - 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@22.0.5': + resolution: {integrity: sha512-+YUOzWeQCwUpV2usFQwHBh01DQYxhcqUpxFKHw73ASIFC4+QsJNX+92++J9PvDOsXWHtGivX+6YmwI9/rcudIA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + deprecated: Angular's Webpack support is deprecated. Use the esbuild and Vite-based "@angular/build" package instead. peerDependencies: - '@angular/compiler-cli': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/localize': ^21.0.0 - '@angular/platform-browser': ^21.0.0 - '@angular/platform-server': ^21.0.0 - '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.1.2 - '@web/test-runner': ^0.20.0 + '@angular/compiler-cli': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/localize': ^22.0.0 + '@angular/platform-browser': ^22.0.0 + '@angular/platform-server': ^22.0.0 + '@angular/service-worker': ^22.0.0 + '@angular/ssr': ^22.0.5 browser-sync: ^3.0.2 - jest: ^30.2.0 - jest-environment-jsdom: ^30.2.0 karma: ^6.3.0 - ng-packagr: ^21.0.0 - protractor: ^7.0.0 + ng-packagr: ^22.0.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 - typescript: '>=5.9 <6.0' + typescript: '>=6.0 <6.1' peerDependenciesMeta: '@angular/core': optional: true @@ -329,124 +315,99 @@ packages: optional: true '@angular/ssr': optional: true - '@web/test-runner': - optional: true browser-sync: optional: true - jest: - optional: true - jest-environment-jsdom: - optional: true karma: optional: true ng-packagr: optional: true - protractor: - optional: true tailwindcss: optional: true - '@angular-devkit/build-webpack@0.2101.2': - resolution: {integrity: sha512-/rC9rcrG+Tn8MZIEW9LTHmBuLiQdCHZyscgqgMXD049qgB858gS1Y/lP/tt0xrP3Yhan5XNcRYjcv6sYPtmPUw==} - 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-webpack@0.2200.5': + resolution: {integrity: sha512-oLNCK+Zpv0/iY9yC/kX9wxZINqdwBQNcidXteC2xtcj99FE0PS8D5zHTZquoGyu9/v91FIt3q1dGtHXSz6MpTg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: webpack: ^5.30.0 webpack-dev-server: ^5.0.2 - '@angular-devkit/core@21.1.2': - resolution: {integrity: sha512-0wl5nJlFWsbwfUB2CQeTSmnVQ8AtqqwM3bYPYtXSc+vA8+hzsOAjjDuRnBxZS9zTnqtXKXB1e7M3Iy7KUwh7LA==} - 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/core@22.0.5': + resolution: {integrity: sha512-xxd3b9X0bbdGuYqDj1OgxtlGotrb/gsxQ5+4aqivWBHq4q/1RI8JfVB7gqcoYTvAfTq63Dwsk7Qd33hC4yJ1Wg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: chokidar: ^5.0.0 peerDependenciesMeta: chokidar: optional: true - '@angular-devkit/core@21.2.12': - resolution: {integrity: sha512-nXms0jVWwHOJK+z6vHvhw7HYFBelxh2gEnkij0OQMABXZN5hoUlTD0DDP1lYR7hQNi8Yb2Ar0UN9ihyUFVM5Kg==} - 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: ^5.0.0 - peerDependenciesMeta: - chokidar: - optional: true + '@angular-devkit/schematics@22.0.5': + resolution: {integrity: sha512-yKkImZuFLIbylmk1REyoW3EWQeqnr6qwtrzs4EPeaJgzjj+MZurq5slQ3fanK9lfX2q+W+dnSQFKBXHxlUGJ8Q==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} - '@angular-devkit/core@21.2.13': - resolution: {integrity: sha512-9jLaHcUr6BumIY9nCsBib1q62p259nf++gd2igYJ7mLm1w/0wEacsZ1cC8wCGEe6vx8a+DrD+EVCQ6zivePG2A==} - 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@22.0.0': + resolution: {integrity: sha512-T2vWQYUhJs6iUlgocHV12OgoxbmN63f17a+tgW+3sYrKN0KAB3xuHsPOoYpRYoWqkVVC44HD441Ju4IDvo8vKg==} peerDependencies: - chokidar: ^5.0.0 - peerDependenciesMeta: - chokidar: - optional: true - - '@angular-devkit/schematics@21.2.12': - resolution: {integrity: sha512-29xe6C9nwHejV9zBcu0js7NmzLWuCFzBGBTmL6eD4JN1NcxEZ/nO1JuaGINjPjzb/UDXPZIqEwHbnFNcGS5v1A==} - 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@21.4.0': - resolution: {integrity: sha512-3kgGmrVaCYbLtDjC8g4BmMBbdz4thsOB8/NYly8JtXM8EuDZEk5Pz6VTRpJR02ARprwayraTTmhyvq6OGBlQ9w==} - peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@angular/cli': '>= 22.0.0 < 23.0.0' + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/bundled-angular-compiler@21.4.0': - resolution: {integrity: sha512-/3H4BPbQ1BHJkkrUsfusZtmHc+qiFWBBZ9UDPWah4xZMjflexOK9U4GYeH7nMjcuyqFnIlMMeJJNwNLGt/hmdg==} + '@angular-eslint/bundled-angular-compiler@22.0.0': + resolution: {integrity: sha512-rv15vGDpGW8zZFaLdhQ+iIO1f0bZds/xvuxoX277hFisXp5Kt6FumJNNIb4g/qxq3xsY46a7fD6R7KvGY3smHg==} - '@angular-eslint/eslint-plugin-template@21.4.0': - resolution: {integrity: sha512-sJEHx2WYnvOgPpzP1eHnUdRS06zgKmRxbiIR0JiCcaSen5iv1HlsMieXy//FS9TtNW+abHOy4UtDuGuSPflPFA==} + '@angular-eslint/eslint-plugin-template@22.0.0': + resolution: {integrity: sha512-y6XL5HJ8C31NpBvkVHpU3bWc+Rk9g1zRtHrs39omhuT29eEUcS3zu47HMFV6tf8rHOI97B2Mstg6qYS5XL9ATg==} peerDependencies: - '@angular-eslint/template-parser': 21.4.0 - '@typescript-eslint/types': ^7.11.0 || ^8.0.0 - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@angular-eslint/template-parser': 22.0.0 + '@typescript-eslint/types': ^8.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/eslint-plugin@21.4.0': - resolution: {integrity: sha512-mow2DMj+xBvGl5t7jzC34R8YfbHbaGNyCNFzpovtl9qc0JbuqLyg6htmt8xb05f8ZjATOr4nz0ESt6HV4c51hw==} + '@angular-eslint/eslint-plugin@22.0.0': + resolution: {integrity: sha512-mKLScPZhqG64ic0KIQoxqSqCdkPwtEZuTOuunvc9lYTw05MJSHRUM2yVFODlCGq97c6BN1F6KBk2I+a+KFnr1g==} peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/schematics@21.4.0': - resolution: {integrity: sha512-crD6Hfxs7x5bN9FCqTZI7uVSiGvprfCS3MCPOpyIQl87bRr/9aNhnicJ3ROUHv+2A713BgPHIgiCII/bxzrfPw==} + '@angular-eslint/schematics@22.0.0': + resolution: {integrity: sha512-gsJQx6c+WIWC5d+NAqn4rRdUzwhinUCTNmCM9x4wygV9DrbAfVG+6OFPEbaDMryNvf0HYDcnGclbIbXjukGCaw==} peerDependencies: - '@angular/cli': '>= 21.0.0 < 22.0.0' + '@angular/cli': '>= 22.0.0 < 23.0.0' - '@angular-eslint/template-parser@21.4.0': - resolution: {integrity: sha512-BaUSLSyS+43fzDoJkTMkGqNdCXq3fGnUZsfXTmrlZPJf5AYFbgAlAPGZXDJyoNWw43fux+DafdlrlKcYUSgSIw==} + '@angular-eslint/template-parser@22.0.0': + resolution: {integrity: sha512-jU5MKQ24bBB4J99gSSexmUrLm2LvTJZCuCHhNTQ1LavWX4e1lrIxhm+6pJILOm6Cixf8jyNXnHMty6nljX8J+Q==} peerDependencies: - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular-eslint/utils@21.4.0': - resolution: {integrity: sha512-7pi+Ga7QmdH5Ig/diau6fR5L4yubgKr9TOjdCg7OeuE/zo0O3osTCNT6JOodzS/iQM1kSCJFDoIBKFeUOttiNw==} + '@angular-eslint/utils@22.0.0': + resolution: {integrity: sha512-VFodMojghnPYm+B3U+HRYrqebPMj8NyobNjVzDdY8V5XIBW+4ivOSEINIz81G48rmm/NZKwj56+bJ88bVX4KIw==} peerDependencies: - '@typescript-eslint/utils': ^7.11.0 || ^8.0.0 - eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + '@typescript-eslint/utils': ^8.0.0 + eslint: ^9.0.0 || ^10.0.0 typescript: '*' - '@angular/build@21.1.2': - resolution: {integrity: sha512-5hl7OTZeQcdkr/3LXSijLuUCwlcqGyYJYOb8GbFqSifSR03JFI3tLQtyQ0LX2CXv3MOx1qFUQbYVfcjW5M36QQ==} - 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/build@22.0.5': + resolution: {integrity: sha512-uVXf1cSKTMDvpiP5x0X8h7D7dICHC2XsbmvRYtxdyTRy+zJduqtCdZTwnwmCrwIc5hOF+bmwa5p17P6XirYVfw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} peerDependencies: - '@angular/compiler': ^21.0.0 - '@angular/compiler-cli': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/localize': ^21.0.0 - '@angular/platform-browser': ^21.0.0 - '@angular/platform-server': ^21.0.0 - '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.1.2 + '@angular/compiler': ^22.0.0 + '@angular/compiler-cli': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/localize': ^22.0.0 + '@angular/platform-browser': ^22.0.0 + '@angular/platform-server': ^22.0.0 + '@angular/service-worker': ^22.0.0 + '@angular/ssr': ^22.0.5 + istanbul-lib-instrument: ^6.0.0 karma: ^6.4.0 less: ^4.2.0 - ng-packagr: ^21.0.0 + ng-packagr: ^22.0.0 postcss: ^8.4.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 tslib: ^2.3.0 - typescript: '>=5.9 <6.0' + typescript: '>=6.0 <6.1' vitest: ^4.0.8 peerDependenciesMeta: '@angular/core': @@ -461,51 +422,7 @@ packages: optional: true '@angular/ssr': optional: true - karma: - optional: true - less: - optional: true - ng-packagr: - optional: true - postcss: - optional: true - tailwindcss: - optional: true - vitest: - optional: true - - '@angular/build@21.2.12': - resolution: {integrity: sha512-zYfo21RuldDWXnshuPfWYtmh5ltlO9+XFHpNObdIInQTFxKD6grLNVNOblFFpi+oIIm4Km+CGSXvBHs/aH0ufA==} - 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': ^21.0.0 - '@angular/compiler-cli': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/localize': ^21.0.0 - '@angular/platform-browser': ^21.0.0 - '@angular/platform-server': ^21.0.0 - '@angular/service-worker': ^21.0.0 - '@angular/ssr': ^21.2.12 - karma: ^6.4.0 - less: ^4.2.0 - ng-packagr: ^21.0.0 - postcss: ^8.4.0 - tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 - tslib: ^2.3.0 - typescript: '>=5.9 <6.0' - vitest: ^4.0.8 - peerDependenciesMeta: - '@angular/core': - optional: true - '@angular/localize': - optional: true - '@angular/platform-browser': - optional: true - '@angular/platform-server': - optional: true - '@angular/service-worker': - optional: true - '@angular/ssr': + istanbul-lib-instrument: optional: true karma: optional: true @@ -520,46 +437,46 @@ packages: vitest: optional: true - '@angular/cdk@21.2.12': - resolution: {integrity: sha512-wB4FLlAdYzQp5htHVKn+fXlNxkFSNw89jPfsJKc15UiadCay6GdzYASLyLqtbk6D4Jz1pBHUpI2ib3mjkCcwxg==} + '@angular/cdk@22.0.3': + resolution: {integrity: sha512-dNPQHkxIM8Hkn028eJxBjBDXFBYJtYjGZCZTG6xGb6OugIuDvPM4hGVqOdXkGnvabfuLNgxbtiSL8l3xXPjUqQ==} peerDependencies: - '@angular/common': ^21.0.0 || ^22.0.0 - '@angular/core': ^21.0.0 || ^22.0.0 - '@angular/platform-browser': ^21.0.0 || ^22.0.0 + '@angular/common': ^22.0.0 || ^23.0.0 + '@angular/core': ^22.0.0 || ^23.0.0 + '@angular/platform-browser': ^22.0.0 || ^23.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/cli@21.2.12': - resolution: {integrity: sha512-oLEL1C1fI39b1eQo5f2cyQhQfE+QMv7dm8z2MmxbP7YR7jAdQPVfGU8CXECR5g7mrYi9WgvIRKB+9Oeq2aH6Jw==} - 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/cli@22.0.5': + resolution: {integrity: sha512-BYg2dWsbqdbpv3UVLNtDns1DoUbsROR8htPPIJEETj4Nfu7VF+g0nnM1CX2Ia+3002xtASgHp4YnRGQN/vAhWA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} hasBin: true - '@angular/common@21.2.17': - resolution: {integrity: sha512-hqAQxRfi5ldFE42suAXRcY+JCANrUh7fuSQ/DtZ7L896id5BT/exuv6dWNBC1PyAfQmRbpD5Pt6/pd+tNLyhDQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/common@22.0.5': + resolution: {integrity: sha512-8HUuQms6bVjjRBF9FScYzepjhesrsPAjJh8UcHaw9YdGwMSn2eUiLZj9V1PnPV9BVu0beBcnAhX50ceeldbcTg==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/core': 21.2.17 + '@angular/core': 22.0.5 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.2.14': - resolution: {integrity: sha512-h+WQfPKFxaDfDhMqUUdOQ1TsDMccav8kLFERmKTRfD4MNOczSMpOMyeXJHCL0Rq4I8WDQvaBJGMG7DXRDefSog==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler-cli@22.0.5': + resolution: {integrity: sha512-DR9kahfd5P/xrXbX8HJu8jBkkUZNqCZuMCod4lyo4a9MTOSKs2ltNU9VEDT7Qw++hDYowr37/lLfIzrvDEFPEQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.14 - typescript: '>=5.9 <6.1' + '@angular/compiler': 22.0.5 + typescript: '>=6.0 <6.1' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.2.17': - resolution: {integrity: sha512-p+NdjYiwAz9Zmu2yul0LlMXaFjMISVVa24+/MVMoKFeQeI82QE8jDywPlnOSHQHvdCcQVpS7saeEriZzX3JuBQ==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/compiler@22.0.5': + resolution: {integrity: sha512-yGhbEBh2WU1kCubjBD+Eo5bwvZPR+zacDhyPao/PgopH0PiVETd4Iv1gK0ZAvg7XdImgzZfipJXKqTK4JbOFiQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} - '@angular/core@21.2.17': - resolution: {integrity: sha512-wYHpwIdnUnjQFOJJNqRcGx7LS3u64jT+R9L0TnMR/ViBM9dQgGYImlSikkftg2yrFCNo5aKRxhG2LLskQurVdg==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/core@22.0.5': + resolution: {integrity: sha512-8cc6CLC1u7kNHpUZA15tak0WPHAZTF16DPslXxDdWbw/WIlDGPsNdTW8Nsaeb/G45mLvBcMwcZpOC70R6iRzmA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/compiler': 21.2.17 + '@angular/compiler': 22.0.5 rxjs: ^6.5.3 || ^7.4.0 zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: @@ -568,50 +485,51 @@ packages: zone.js: optional: true - '@angular/forms@21.2.14': - resolution: {integrity: sha512-HQYIybyMt0CrI31rW6vXbiDsSM2DDtTcOVeT/nWDRNCoqBrREDg8rVsm2Y+fUMsiQVJNa6dCXPwvYhjzJ4r7ug==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/forms@22.0.5': + resolution: {integrity: sha512-kdmLo956pUMG6ZS0v9crAu16pEzBrz6i7rdfCsgI6A3lcfOWNTZLysMGHHiGGqfdP6LJR88ziHIEKrgtkCkK1A==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 21.2.14 - '@angular/core': 21.2.14 - '@angular/platform-browser': 21.2.14 + '@angular/common': 22.0.5 + '@angular/core': 22.0.5 + '@angular/platform-browser': 22.0.5 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.2.14': - resolution: {integrity: sha512-cVqQag+O8mIuH5Dz8Lw4uElWtFogr+xgUFbakI3QQAbnLaqeQaDqHvXE8VGkTmhGvEMnE0cvPn+rOQ/N5exzbw==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/localize@22.0.5': + resolution: {integrity: sha512-jpJqSCdAt1TPg8Zea+tkmqdfZF+qJhlGlQylYCrT56NLBUf0qqAl32KjxlBjZvFrUm7AqNkHN7L+E/YMcXmwzw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.2.14 - '@angular/compiler-cli': 21.2.14 + '@angular/compiler': 22.0.5 + '@angular/compiler-cli': 22.0.5 - '@angular/platform-browser-dynamic@21.2.14': - resolution: {integrity: sha512-m5U4zX8JFnxTAIGpsBXIAyefSmYqdORY/OfHC0aMmZovuFCbXXIYqYRQDBB7+YVNpSDSHllCrKEZFu/CC6dq3g==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser-dynamic@22.0.5': + resolution: {integrity: sha512-KEhGMknnX30assVhIfp04cxKrkdV5ngtWjRs4suNs0ev9vBmcj+KXtfEGGnW+2i4TjnaB4veGUxnq/snoG+AtA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} + deprecated: '@angular/platform-browser-dynamic is deprecated. Use `@angular/platform-browser` instead.' peerDependencies: - '@angular/common': 21.2.14 - '@angular/compiler': 21.2.14 - '@angular/core': 21.2.14 - '@angular/platform-browser': 21.2.14 + '@angular/common': 22.0.5 + '@angular/compiler': 22.0.5 + '@angular/core': 22.0.5 + '@angular/platform-browser': 22.0.5 - '@angular/platform-browser@21.2.14': - resolution: {integrity: sha512-34tBwxh86yN2YifBDhCesm6N+nn9WcbuXjRwfo0mTme15OZ/zt56yw7v1mcK3UFLegIIALtsIgpXXrPWWQoKkA==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/platform-browser@22.0.5': + resolution: {integrity: sha512-Jm0YQ0NsJl3vhzziO+BEmtW5AYIyI+jVGNJHs4nFKLqSPG7DWkBfx4EykHBN5pAR4NLwwaJyFgbgNE/epZJDZQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/animations': 21.2.14 - '@angular/common': 21.2.14 - '@angular/core': 21.2.14 + '@angular/animations': 22.0.5 + '@angular/common': 22.0.5 + '@angular/core': 22.0.5 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/router@21.2.14': - resolution: {integrity: sha512-Yo3LdgcqkfMu2/Ycl8o/4QjCBqZhtA+a7B8JVdW5cWdrpFTxKCOrzm+YRUMuIFmH5nzSv9oGnUuz64uk1+7r5Q==} - engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} + '@angular/router@22.0.5': + resolution: {integrity: sha512-1S5PCHicRQkQX4z/zjBu9VHMSoT9kOkWI0kuXuswiZtcFhK7kZbVvzrP/dBjc2YUg3AlsxS8iZY6fKFzj3/nBA==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0} peerDependencies: - '@angular/common': 21.2.14 - '@angular/core': 21.2.14 - '@angular/platform-browser': 21.2.14 + '@angular/common': 22.0.5 + '@angular/core': 22.0.5 + '@angular/platform-browser': 22.0.5 rxjs: ^6.5.3 || ^7.4.0 '@asamuzakjp/css-color@3.2.0': @@ -621,18 +539,10 @@ packages: resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.0': - resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} - engines: {node: '>=6.9.0'} - '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.5': - resolution: {integrity: sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==} - engines: {node: '>=6.9.0'} - '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} @@ -641,8 +551,8 @@ packages: resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.5': - resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} '@babel/generator@7.29.7': @@ -669,8 +579,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.6': - resolution: {integrity: sha512-mOAsxeeKkUKayvZR3HeTYD/fICpCPLJrU5ZjelT/PA6WHtNDBOE436YiaEUvHN454bRM3CebhDsIpieCc4texA==} + '@babel/helper-define-polyfill-provider@0.6.8': + resolution: {integrity: sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -682,10 +592,6 @@ packages: resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.28.6': - resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} - engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.29.7': resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} @@ -700,10 +606,6 @@ packages: resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.28.6': - resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} - engines: {node: '>=6.9.0'} - '@babel/helper-plugin-utils@7.29.7': resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} @@ -720,8 +622,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': - resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': + resolution: {integrity: sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==} engines: {node: '>=6.9.0'} '@babel/helper-split-export-declaration@7.24.7': @@ -736,10 +638,6 @@ packages: resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.27.1': - resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} - engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} @@ -775,6 +673,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7': + resolution: {integrity: sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1': resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==} engines: {node: '>=6.9.0'} @@ -820,12 +724,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.28.6': - resolution: {integrity: sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.29.7': resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} engines: {node: '>=6.9.0'} @@ -908,14 +806,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.28.0': - resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + '@babel/plugin-transform-async-generator-functions@7.29.0': + resolution: {integrity: sha512-va0VdWro4zlBr2JsXC+ofCPB2iG12wPtVGTWFx2WLDOM3nYQZZIGP82qku2eW/JR83sD+k2k+CsNtyEbUqhU6w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-to-generator@7.27.1': - resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==} + '@babel/plugin-transform-async-to-generator@7.28.6': + resolution: {integrity: sha512-ilTRcmbuXjsMmcZ3HASTe4caH5Tpo93PkTxF9oG2VZsSWsahydmcEHhix9Ik122RcTnZnUzPbmux4wh1swfv7g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1154,8 +1052,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-runtime@7.28.5': - resolution: {integrity: sha512-20NUVgOrinudkIBzQ2bNxP08YpKprUkRTiRSd2/Z5GOdPImJGkoN4Z7IQe1T5AdyKI1i5L6RBmluqdSzvaq9/w==} + '@babel/plugin-transform-runtime@7.29.0': + resolution: {integrity: sha512-jlaRT5dJtMaMCV6fAuLbsQMSwz/QkvaHOHOSXRitGGwSpR1blCY4KUKoyP2tYO8vJcqYe8cEj96cqSztv3uF9w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1214,8 +1112,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.28.5': - resolution: {integrity: sha512-S36mOoi1Sb6Fz98fBfE+UZSpYw5mJm0NUHtIKrOuNcqeFauy1J6dIvXm2KRVKobOSaGq4t/hBXdN4HGU3wL9Wg==} + '@babel/preset-env@7.29.3': + resolution: {integrity: sha512-ySZypNLAIH1ClygLDQzVMoGQRViATnkHkYYV6TcNDz+8+jwZCdsguGvsb3EY5d9wyWyhmF1iSuFM0Yh5XPnqSA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1225,8 +1123,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - '@babel/runtime@7.28.4': - resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.29.7': @@ -1290,8 +1188,8 @@ packages: resolution: {integrity: sha512-kzyuwOAQnXJNLS9PSyrk0CWk35nWJW/zl/6KvnTBMFK65gm7U1/Z5BqjxeapjZCIhQcM/DsrEmcbRwDyXyXK4A==} engines: {node: '>=14'} - '@discoveryjs/json-ext@0.6.3': - resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} + '@discoveryjs/json-ext@1.1.0': + resolution: {integrity: sha512-Xc3VhU02wqZ1HvHRJUwL09HkZSTvidqY5Ya0NXBSYOxAp+Ln9dcJr9fySI+CkONzP3PekQo9WdzCv0PGER/mOA==} engines: {node: '>=14.17.0'} '@emnapi/core@1.10.0': @@ -1303,470 +1201,314 @@ packages: '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} - '@esbuild/aix-ppc64@0.27.2': - resolution: {integrity: sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - '@esbuild/aix-ppc64@0.27.3': resolution: {integrity: sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.28.0': - resolution: {integrity: sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==} + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.27.2': - resolution: {integrity: sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - '@esbuild/android-arm64@0.27.3': resolution: {integrity: sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.28.0': - resolution: {integrity: sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==} + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.27.2': - resolution: {integrity: sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - '@esbuild/android-arm@0.27.3': resolution: {integrity: sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-arm@0.28.0': - resolution: {integrity: sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==} + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.27.2': - resolution: {integrity: sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - '@esbuild/android-x64@0.27.3': resolution: {integrity: sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/android-x64@0.28.0': - resolution: {integrity: sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==} + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.27.2': - resolution: {integrity: sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - '@esbuild/darwin-arm64@0.27.3': resolution: {integrity: sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.28.0': - resolution: {integrity: sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==} + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.27.2': - resolution: {integrity: sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - '@esbuild/darwin-x64@0.27.3': resolution: {integrity: sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.28.0': - resolution: {integrity: sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==} + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.27.2': - resolution: {integrity: sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - '@esbuild/freebsd-arm64@0.27.3': resolution: {integrity: sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.28.0': - resolution: {integrity: sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==} + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.27.2': - resolution: {integrity: sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - '@esbuild/freebsd-x64@0.27.3': resolution: {integrity: sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.28.0': - resolution: {integrity: sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==} + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.27.2': - resolution: {integrity: sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - '@esbuild/linux-arm64@0.27.3': resolution: {integrity: sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.28.0': - resolution: {integrity: sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==} + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.27.2': - resolution: {integrity: sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - '@esbuild/linux-arm@0.27.3': resolution: {integrity: sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.28.0': - resolution: {integrity: sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==} + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.27.2': - resolution: {integrity: sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - '@esbuild/linux-ia32@0.27.3': resolution: {integrity: sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.28.0': - resolution: {integrity: sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==} + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.27.2': - resolution: {integrity: sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - '@esbuild/linux-loong64@0.27.3': resolution: {integrity: sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.28.0': - resolution: {integrity: sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==} + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.27.2': - resolution: {integrity: sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - '@esbuild/linux-mips64el@0.27.3': resolution: {integrity: sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.28.0': - resolution: {integrity: sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==} + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.27.2': - resolution: {integrity: sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - '@esbuild/linux-ppc64@0.27.3': resolution: {integrity: sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.28.0': - resolution: {integrity: sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==} + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.27.2': - resolution: {integrity: sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - '@esbuild/linux-riscv64@0.27.3': resolution: {integrity: sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.28.0': - resolution: {integrity: sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==} + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.27.2': - resolution: {integrity: sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - '@esbuild/linux-s390x@0.27.3': resolution: {integrity: sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.28.0': - resolution: {integrity: sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==} + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.27.2': - resolution: {integrity: sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - '@esbuild/linux-x64@0.27.3': resolution: {integrity: sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.28.0': - resolution: {integrity: sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==} + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.27.2': - resolution: {integrity: sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - '@esbuild/netbsd-arm64@0.27.3': resolution: {integrity: sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-arm64@0.28.0': - resolution: {integrity: sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==} + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.27.2': - resolution: {integrity: sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - '@esbuild/netbsd-x64@0.27.3': resolution: {integrity: sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.28.0': - resolution: {integrity: sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==} + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.27.2': - resolution: {integrity: sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - '@esbuild/openbsd-arm64@0.27.3': resolution: {integrity: sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-arm64@0.28.0': - resolution: {integrity: sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==} + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.27.2': - resolution: {integrity: sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - '@esbuild/openbsd-x64@0.27.3': resolution: {integrity: sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.28.0': - resolution: {integrity: sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==} + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.27.2': - resolution: {integrity: sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - '@esbuild/openharmony-arm64@0.27.3': resolution: {integrity: sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/openharmony-arm64@0.28.0': - resolution: {integrity: sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==} + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.27.2': - resolution: {integrity: sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - '@esbuild/sunos-x64@0.27.3': resolution: {integrity: sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.28.0': - resolution: {integrity: sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==} + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.27.2': - resolution: {integrity: sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - '@esbuild/win32-arm64@0.27.3': resolution: {integrity: sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.28.0': - resolution: {integrity: sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==} + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.27.2': - resolution: {integrity: sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - '@esbuild/win32-ia32@0.27.3': resolution: {integrity: sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.28.0': - resolution: {integrity: sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==} + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.27.2': - resolution: {integrity: sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@esbuild/win32-x64@0.27.3': resolution: {integrity: sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.28.0': - resolution: {integrity: sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==} + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1834,134 +1576,143 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/ansi@1.0.2': - resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} - engines: {node: '>=18'} + '@inquirer/ansi@2.0.7': + resolution: {integrity: sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} - '@inquirer/checkbox@4.3.2': - resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} - engines: {node: '>=18'} + '@inquirer/checkbox@5.2.1': + resolution: {integrity: sha512-b6xmA/VlTe0ZgDQHDui+Nav470u7u49nRd8/iuhOcQPO9Ch7lGuogydhi2VOmNlZ+zXcM8IcPuNSwQcdJaF/kw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/confirm@5.1.21': - resolution: {integrity: sha512-KR8edRkIsUayMXV+o3Gv+q4jlhENF9nMYUZs9PA2HzrXeHI8M5uDag70U7RJn9yyiMZSbtF5/UexBtAVtZGSbQ==} - engines: {node: '>=18'} + '@inquirer/confirm@6.0.12': + resolution: {integrity: sha512-h9FgGun3QwVYNj5TWIZZ+slii73bMoBFjPfVIGtnFuL4t8gBiNDV9PcSfIzkuxvgquJKt9nr1QzszpBzTbH8Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/core@10.3.2': - resolution: {integrity: sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==} - engines: {node: '>=18'} + '@inquirer/confirm@6.1.1': + resolution: {integrity: sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/editor@4.2.23': - resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} - engines: {node: '>=18'} + '@inquirer/core@11.2.1': + resolution: {integrity: sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/expand@4.0.23': - resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} - engines: {node: '>=18'} + '@inquirer/editor@5.2.2': + resolution: {integrity: sha512-ZRVd/oD+sYsUd5zVm0NflqEzlqfYCyHNsqkHl2oWXEUHs12tCbcSFi+wVFEvD8+LGRaMUsVrE7qeo6lSG/S1Vg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/external-editor@1.0.3': - resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} - engines: {node: '>=18'} + '@inquirer/expand@5.1.1': + resolution: {integrity: sha512-YmQpenjbFSHAK3sOd44puHh3V1KXXr+JiNpUztoSQ4drLh2rTVzTap/YtlAVu/5xavifIlBfNEzJ/neZJ1a/1g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/figures@1.0.15': - resolution: {integrity: sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==} - engines: {node: '>=18'} - - '@inquirer/input@4.3.1': - resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} - engines: {node: '>=18'} + '@inquirer/external-editor@3.0.3': + resolution: {integrity: sha512-6thf5I8q7lZwzGLAxPaaGEREEkZ3nyePPDQ1oyobblxmEE8mqTLguScP7pDjUTAibiyb4hfXl+qjUEJ+di/aNA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/number@3.0.23': - resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} - engines: {node: '>=18'} + '@inquirer/figures@2.0.7': + resolution: {integrity: sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + + '@inquirer/input@5.1.2': + resolution: {integrity: sha512-9K/DDBSQpOyZSkt6sOVP9Vo0TR7atX2kuILsUu0x3wVcVbe97lJwIJKMLdMw25tDYuXl/qp6erT0Xs1rfmcfZg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/password@4.0.23': - resolution: {integrity: sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==} - engines: {node: '>=18'} + '@inquirer/number@4.1.1': + resolution: {integrity: sha512-XF4IXAbPnGPgw0wsbC/i2tPcyfdZgDpUlhsqU0SfT4IRIGWha6Xm9VRgN5yYxJq+jnyXlfXI/nQ3ulfk0iEICA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/prompts@7.10.1': - resolution: {integrity: sha512-Dx/y9bCQcXLI5ooQ5KyvA4FTgeo2jYj/7plWfV5Ak5wDPKQZgudKez2ixyfz7tKXzcJciTxqLeK7R9HItwiByg==} - engines: {node: '>=18'} + '@inquirer/password@5.1.1': + resolution: {integrity: sha512-3XBfF7DAsp5qeDsvN5Rd1HmbNokVvEQoUM0QLrRcybC9nX96w3Pbmu7qUsb3IT3J3jBvs2+mTXaKHOUsgHMLzg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/rawlist@4.1.11': - resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} - engines: {node: '>=18'} + '@inquirer/prompts@8.4.2': + resolution: {integrity: sha512-XJmn/wY4AX56l1BRU+ZjDrFtg9+2uBEi4JvJQj82kwJDQKiPgSn4CEsbfGGygS4Gw6rkL4W18oATjfVfaqub2Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/search@3.2.2': - resolution: {integrity: sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==} - engines: {node: '>=18'} + '@inquirer/rawlist@5.3.1': + resolution: {integrity: sha512-QqdTqQddL3qPX/PPrjobpsO25NZ4dWXgTLenrR445L2ptLEYE6Z+PD5c5CNDJNx4ugRgELAIpSIJxZaO2jJ2Og==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/select@4.4.2': - resolution: {integrity: sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==} - engines: {node: '>=18'} + '@inquirer/search@4.2.1': + resolution: {integrity: sha512-xJj8QWKRSrfKoBIITLZK61dD3zwo0Rz11fgDImku30/Oe81zMdIdGgrLY2h6RkJ+KZ/GhNYIRMKnH/62qBTA5g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: '@types/node': optional: true - '@inquirer/type@3.0.10': - resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} - engines: {node: '>=18'} + '@inquirer/select@5.2.1': + resolution: {integrity: sha512-FlDndEUww8m7BfukO2nJa25vhD+H5jxxCv4oGioKqzyWz3nPHhhw4LKdYRSlXuAx7DsdWia7iyaBPKKS95Evfw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@4.0.7': + resolution: {integrity: sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^20.17.0'} peerDependencies: '@types/node': '>=18' peerDependenciesMeta: @@ -2249,85 +2000,50 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} - '@listr2/prompt-adapter-inquirer@3.0.5': - resolution: {integrity: sha512-WELs+hj6xcilkloBXYf9XXK8tYEnKsgLj01Xl5ONUJpKjmT5hGVUzNUS5tooUxs7pGMrw+jFD/41WpqW4V3LDA==} - engines: {node: '>=20.0.0'} + '@listr2/prompt-adapter-inquirer@4.2.3': + resolution: {integrity: sha512-Co9U3AJ3LW0J8XBHjVoNKA79dMAyFt8EZH3OaKTMcDTj8r+6kG3vSUPq/eGLHT7P0iK3uLaFfhdFYd3033P24g==} + engines: {node: '>=22.13.0'} peerDependencies: - '@inquirer/prompts': '>= 3 < 8' - listr2: 9.0.5 + '@inquirer/prompts': '>= 3 < 9' + listr2: 10.2.1 - '@lmdb/lmdb-darwin-arm64@3.4.4': - resolution: {integrity: sha512-XaKL705gDWd6XVls3ATDj13ZdML/LqSIxwgnYpG8xTzH2ifArx8fMMDdvqGE/Emd+W6R90W2fveZcJ0AyS8Y0w==} + '@lmdb/lmdb-darwin-arm64@3.5.4': + resolution: {integrity: sha512-Kk4Kz3iyu1QiLsLZBS9Af1eSKUC8VR2T+/jyE2iAyuGw2VwK08pp5iTbZnXn6sWu0LogO/RFktMxOjiDA2sS3w==} cpu: [arm64] os: [darwin] - '@lmdb/lmdb-darwin-arm64@3.5.1': - resolution: {integrity: sha512-tpfN4kKrrMpQ+If1l8bhmoNkECJi0iOu6AEdrTJvWVC+32sLxTARX5Rsu579mPImRP9YFWfWgeRQ5oav7zApQQ==} - cpu: [arm64] - os: [darwin] - - '@lmdb/lmdb-darwin-x64@3.4.4': - resolution: {integrity: sha512-GPHGEVcwJlkD01GmIr7B4kvbIcUDS2+kBadVEd7lU4can1RZaZQLDDBJRrrNfS2Kavvl0VLI/cMv7UASAXGrww==} + '@lmdb/lmdb-darwin-x64@3.5.4': + resolution: {integrity: sha512-BEe5Rp3trn26oxoXOVL5HVDoiYmjUDwr8NRPkBOdUdCSBEorKI+7JrZLRKAdxO+G6cGQLgseXk0gR7qIQa7aGw==} cpu: [x64] os: [darwin] - '@lmdb/lmdb-darwin-x64@3.5.1': - resolution: {integrity: sha512-+a2tTfc3rmWhLAolFUWRgJtpSuu+Fw/yjn4rF406NMxhfjbMuiOUTDRvRlMFV+DzyjkwnokisskHbCWkS3Ly5w==} - cpu: [x64] - os: [darwin] - - '@lmdb/lmdb-linux-arm64@3.4.4': - resolution: {integrity: sha512-mALqr7DE42HsiwVTKpQWxacjHoJk+e9p00RWIJqTACh/hpucxp/0lK/XMh5XzWnU/TDCZLukq1+vNqnNumTP/Q==} + '@lmdb/lmdb-linux-arm64@3.5.4': + resolution: {integrity: sha512-cUXEengO8o60v1SWerJTH4/RH4U3+9jC0/4njp2Z9NdmvaGzhKsbRM2wpXuRYrN8tytsoJCg0SvWEWwHAwLbCA==} cpu: [arm64] os: [linux] - '@lmdb/lmdb-linux-arm64@3.5.1': - resolution: {integrity: sha512-aoERa5B6ywXdyFeYGQ1gbQpkMkDbEo45qVoXE5QpIRavqjnyPwjOulMkmkypkmsbJ5z4Wi0TBztON8agCTG0Vg==} - cpu: [arm64] - os: [linux] - - '@lmdb/lmdb-linux-arm@3.4.4': - resolution: {integrity: sha512-cmev5/dZr5ACKri9f6GU6lZCXTjMhV72xujlbOhFCgFXrt4W0TxGsmY8kA1BITvH60JBKE50cSxsiulybAbrrw==} + '@lmdb/lmdb-linux-arm@3.5.4': + resolution: {integrity: sha512-SGbFR7816uBcTHc2ZY4S6WyOkl9bICnzqTQd2Mv4V/j24cfds88xx2nC6cm/y8zGQL7Ds31YF/5NGxjgcdM5Hw==} cpu: [arm] os: [linux] - '@lmdb/lmdb-linux-arm@3.5.1': - resolution: {integrity: sha512-0EgcE6reYr8InjD7V37EgXcYrloqpxVPINy3ig1MwDSbl6LF/vXTYRH9OE1Ti1D8YZnB35ZH9aTcdfSb5lql2A==} - cpu: [arm] - os: [linux] - - '@lmdb/lmdb-linux-x64@3.4.4': - resolution: {integrity: sha512-QjLs8OcmCNcraAcLoZyFlo0atzBJniQLLwhtR+ymQqS5kLYpV5RqwriL87BW+ZiR9ZiGgZx3evrz5vnWPtJ1fQ==} + '@lmdb/lmdb-linux-x64@3.5.4': + resolution: {integrity: sha512-Gxq8jpgOWXwd0PUR+c9R2Ik1/uBnGd5GMIIzRRDqABCkvmjtC3KWcyhesV9jSPCz759isl0NlbsstZ2oyvk8lA==} cpu: [x64] os: [linux] - '@lmdb/lmdb-linux-x64@3.5.1': - resolution: {integrity: sha512-SqNDY1+vpji7bh0sFH5wlWyFTOzjbDOl0/kB5RLLYDAFyd/uw3n7wyrmas3rYPpAW7z18lMOi1yKlTPv967E3g==} - cpu: [x64] - os: [linux] - - '@lmdb/lmdb-win32-arm64@3.4.4': - resolution: {integrity: sha512-tr/pwHDlZ33forLGAr0tI04cRmP4SgF93yHbb+2zvZiDEyln5yMHhbKDySxY66aUOkhvBvTuHq9q/3YmTj6ZHQ==} + '@lmdb/lmdb-win32-arm64@3.5.4': + resolution: {integrity: sha512-pKv1DJ1bPZAaHkdFsSz5IDfUG8x9vntgquXF9/Dm2xuupcIe/EkLzylpoBxppFVK5vzbV561Dq26jNY2fIMA7g==} cpu: [arm64] os: [win32] - '@lmdb/lmdb-win32-arm64@3.5.1': - resolution: {integrity: sha512-50v0O1Lt37cwrmR9vWZK5hRW0Aw+KEmxJJ75fge/zIYdvNKB/0bSMSVR5Uc2OV9JhosIUyklOmrEvavwNJ8D6w==} - cpu: [arm64] - os: [win32] - - '@lmdb/lmdb-win32-x64@3.4.4': - resolution: {integrity: sha512-KRzfocJzB/mgoTCqnMawuLSKheHRVTqWfSmouIgYpFs6Hx4zvZSvsZKSCEb5gHmICy7qsx9l06jk3MFTtiFVAQ==} + '@lmdb/lmdb-win32-x64@3.5.4': + resolution: {integrity: sha512-JF1BmLCm9kGEVZgYmJq43zeQVdHVgAJnTi/NURWEsy6L1ZrrlSmdltS+D17QN4LODwf+1LMXAA9auIZVXtWwzw==} cpu: [x64] os: [win32] - '@lmdb/lmdb-win32-x64@3.5.1': - resolution: {integrity: sha512-qwosvPyl+zpUlp3gRb7UcJ3H8S28XHCzkv0Y0EgQToXjQP91ZD67EHSCDmaLjtKhe+GVIW5om1KUpzVLA0l6pg==} - cpu: [x64] - os: [win32] - - '@modelcontextprotocol/sdk@1.26.0': - resolution: {integrity: sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 @@ -2366,79 +2082,74 @@ packages: cpu: [x64] os: [win32] - '@napi-rs/canvas-android-arm64@0.1.100': - resolution: {integrity: sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ==} + '@napi-rs/canvas-android-arm64@1.0.2': + resolution: {integrity: sha512-IMXKVQod0ol4vt3gmClUfXz4JAgHYESGPCUqmH3lQxBoL0K/2greJaQE1HVBVxWWFKfLc4OLZVdxg7kXVyXv+g==} engines: {node: '>= 10'} cpu: [arm64] os: [android] - '@napi-rs/canvas-darwin-arm64@0.1.100': - resolution: {integrity: sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A==} + '@napi-rs/canvas-darwin-arm64@1.0.2': + resolution: {integrity: sha512-Sc8tPi6cF+5lqOzCCKFALJHhDiRwyMzTPYm3bbhdXsOunU0lQO5f05ucyOzN2r55I23Hg5bsjH63uSCvWp3EgQ==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@napi-rs/canvas-darwin-x64@0.1.100': - resolution: {integrity: sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw==} + '@napi-rs/canvas-darwin-x64@1.0.2': + resolution: {integrity: sha512-niDXZ9LhKB1zLrUdYB64RHQFDGz9rr0eGx061qtJJU3U20EMMIx28ADF5fVYbhtOgkWQrBjFicfaye1yM0U62A==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': - resolution: {integrity: sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA==} + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.2': + resolution: {integrity: sha512-sgatQL9JxGRH/Amzcvu0P3t8Am3duou74CisfuJ41Dwt8cWy723z/9KZ8LlgmxfypEwEZxSTNFJtU8d281lmhQ==} engines: {node: '>= 10'} cpu: [arm] os: [linux] - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': - resolution: {integrity: sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw==} + '@napi-rs/canvas-linux-arm64-gnu@1.0.2': + resolution: {integrity: sha512-dgKuX0peF3xwY6ZF5QxGS4wbfDqpoFAJYXiLSp+guZKARQUKMkRqZSDrXKj7nfrec3UCMzC0PFCPte0ES98AiA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] - '@napi-rs/canvas-linux-arm64-musl@0.1.100': - resolution: {integrity: sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ==} + '@napi-rs/canvas-linux-arm64-musl@1.0.2': + resolution: {integrity: sha512-qwROoDIC9upfvDoRLuPn2aNg9CGW1x0Ygr4k2Or+8paA9d0qBLwk87U+g8KQpoOviKoPoiwl97kvBYuYD7qZoA==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': - resolution: {integrity: sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw==} + '@napi-rs/canvas-linux-riscv64-gnu@1.0.2': + resolution: {integrity: sha512-fXRjnPihdnbO6qy1QQOgxAonb68A0TCEG7rj1x7v7rxNElsE8EVIKIEUTvyDtU+sthYSbX+8e7g3oZiLGnOmxw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - libc: [glibc] - '@napi-rs/canvas-linux-x64-gnu@0.1.100': - resolution: {integrity: sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg==} + '@napi-rs/canvas-linux-x64-gnu@1.0.2': + resolution: {integrity: sha512-nPR97DXhbWIAy7yazF3jc06kEPMqYMLmPzFOVNlwKPfIoSChnI+x7dc0hTLaihz3jxrjL6j4BbA7earxfx4X3g==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] - '@napi-rs/canvas-linux-x64-musl@0.1.100': - resolution: {integrity: sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA==} + '@napi-rs/canvas-linux-x64-musl@1.0.2': + resolution: {integrity: sha512-l7zZY5+jL5qnBZtDz7CoBtY6p7EkHu422g/0zWwrOrzIwWyWxZFRfZZORY1UG7YApymPLx+UbOkN206xXn/c1Q==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': - resolution: {integrity: sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw==} + '@napi-rs/canvas-win32-arm64-msvc@1.0.2': + resolution: {integrity: sha512-yE0koHCFF4PIbMc2o2SEALhnipz7WBISh5glLvQiomtIoCcW0np3H4Lw93ceJAfJttTTeIIWFbwH84F7EVzjMQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@napi-rs/canvas-win32-x64-msvc@0.1.100': - resolution: {integrity: sha512-MyT1j3mHC2+Lu4pBi9mKyMJhtP6U7k7EldY7sj/uS5gJA65gTXt8MefJQXLJo5d/vZbuWmfxzkEUNc/urV3pHA==} + '@napi-rs/canvas-win32-x64-msvc@1.0.2': + resolution: {integrity: sha512-okU8/t2foV6C31n0GtvEMbfD5rOFc70+/6xUNME9Guld29sgSOIGUEDScAWFlcP3k5TYQRl9TNkwJEEjh15w8A==} engines: {node: '>= 10'} cpu: [x64] os: [win32] - '@napi-rs/canvas@0.1.100': - resolution: {integrity: sha512-xglYA6q3XO5P3BNJYxVZ1IV7DLVjp1Py6nwag88YntrS+3vKHyYcMqXVS4ZztJmwz2uGvz1FWhI/4LgbR5uQDA==} + '@napi-rs/canvas@1.0.2': + resolution: {integrity: sha512-EYEqlMYaCbpZDz+IgDH5xp9MTd3ui4dmGqbQYryhMLnSRxrhHKq5KQWHHKxFUcEP4Hp8/BWgvqXocX4j7iSbOQ==} engines: {node: '>= 10'} '@napi-rs/nice-android-arm-eabi@1.1.1': @@ -2482,49 +2193,42 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-arm64-musl@1.1.1': resolution: {integrity: sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@napi-rs/nice-linux-ppc64-gnu@1.1.1': resolution: {integrity: sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==} engines: {node: '>= 10'} cpu: [ppc64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-riscv64-gnu@1.1.1': resolution: {integrity: sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==} engines: {node: '>= 10'} cpu: [riscv64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-s390x-gnu@1.1.1': resolution: {integrity: sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==} engines: {node: '>= 10'} cpu: [s390x] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-x64-gnu@1.1.1': resolution: {integrity: sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@napi-rs/nice-linux-x64-musl@1.1.1': resolution: {integrity: sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@napi-rs/nice-openharmony-arm64@1.1.1': resolution: {integrity: sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==} @@ -2560,23 +2264,23 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@ng-bootstrap/ng-bootstrap@20.0.0': - resolution: {integrity: sha512-Jt+GUQ0PdM8VsOUUVr7vTQXhwcGwe2DCe1mmfS21vz9pLSOtGRz41ohZKc1egUevj5Rxm2sHVq5Sve68/nTMfA==} + '@ng-bootstrap/ng-bootstrap@21.0.0': + resolution: {integrity: sha512-H7lGYGQGERoX5PeU1nf99U8tvPBJwQod6G99NoaJYwhBtKDoA/W0auUJqcbPMOGnO3gHtjtnOCji49tRbUIbPw==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/forms': ^21.0.0 - '@angular/localize': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/forms': ^22.0.0 + '@angular/localize': ^22.0.0 '@popperjs/core': ^2.11.8 rxjs: ^6.5.3 || ^7.4.0 - '@ng-select/ng-select@21.8.2': - resolution: {integrity: sha512-Gk5xlVKLOG3WTIgOteduwlKBZTt0njkoUXheKgUSKn+h+poYQAf61uENCK08Tj0XWxes9BQUc5J+R2GC+86jgA==} - engines: {node: ^20.19.0 || ^22.12.0 || ^24.0.0} + '@ng-select/ng-select@23.2.0': + resolution: {integrity: sha512-AM1/60UnZmowGvehrr4f8Ff0PJlK5doQh1LH6rTche+DhquaSkG2WBEWFfyK0ujIXNdjeWx+9yf2RcFSZWZ+Wg==} + engines: {node: ^22.12.0 || ^24.0.0 || ^26.0.0} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 - '@angular/forms': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/forms': ^22.0.0 '@ngneat/dirty-check-forms@3.0.3': resolution: {integrity: sha512-YGlKrAaqTRO8lfT1xyN9LkYN0GH0crzdnXAxQFNEuNDQpCHv9cQ0j9XPDsonek6X4K7fLug84n0CQ42rSmGBqw==} @@ -2587,14 +2291,19 @@ packages: lodash-es: '>=4.17.0' rxjs: '>=6.0.0' - '@ngtools/webpack@21.1.2': - resolution: {integrity: sha512-ZNMMD35urDKqYtx1drxPyGAvUPMOoiKjvrH8owpN+mzIO1nYpssCgmAseo1hePAduSvv8tAsY1NLtJfMSNzubw==} - 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'} + '@ngtools/webpack@22.0.5': + resolution: {integrity: sha512-f0vNCwM4Z4GIl1XORd2z9TyMiq1Vs5lcK9Se4joQiTPd5yEIS1+UPDhJ1Lh38J+nXzdJyieZzQi0GjmS6Iv7Cw==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} + deprecated: Angular's Webpack support is deprecated. Use the esbuild and Vite-based "@angular/build" package instead. peerDependencies: - '@angular/compiler-cli': ^21.0.0 - typescript: '>=5.9 <6.0' + '@angular/compiler-cli': ^22.0.0 + typescript: '>=6.0 <6.1' webpack: ^5.54.0 + '@noble/hashes@1.4.0': + resolution: {integrity: sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==} + engines: {node: '>= 16'} + '@npmcli/agent@4.0.2': resolution: {integrity: sha512-EUEuWAxnL07Sp5/iC/1X6Xj+XThUvnbei9zfRWZdEXa7lss9RTHMhAHBeg+MZ5To9s/gGaSI+UwZTPdYMvKSeg==} engines: {node: ^20.17.0 || >=22.9.0} @@ -2674,12 +2383,6 @@ packages: '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} - '@oxc-project/types@0.106.0': - resolution: {integrity: sha512-QdsH3rZq480VnOHSHgPYOhjL8O8LBdcnSjM408BpPCCUc0JYYZPG9Gafl9i3OcGk/7137o+gweb4cCv3WAUykg==} - - '@oxc-project/types@0.113.0': - resolution: {integrity: sha512-Tp3XmgxwNQ9pEN9vxgJBAqdRamHibi76iowQ38O2I4PMpcvNRQNVsU2n1x1nv9yh0XoTrGFzf7cZSGxmixxrhA==} - '@parcel/watcher-android-arm64@2.5.6': resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} @@ -2709,42 +2412,36 @@ packages: engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm-musl@2.5.6': resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - libc: [musl] '@parcel/watcher-linux-arm64-glibc@2.5.6': resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-arm64-musl@2.5.6': resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - libc: [musl] '@parcel/watcher-linux-x64-glibc@2.5.6': resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [glibc] '@parcel/watcher-linux-x64-musl@2.5.6': resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - libc: [musl] '@parcel/watcher-win32-arm64@2.5.6': resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} @@ -2768,6 +2465,43 @@ packages: resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} + '@peculiar/asn1-cms@2.8.0': + resolution: {integrity: sha512-NgekZOrSJFSBFLFoLfwePguAWAx7z1+f2TEsWFUMyiqqfntZ4+S/S5hzqME3q4pCA0iOsFKdwiQ35dwY24eVqA==} + + '@peculiar/asn1-csr@2.8.0': + resolution: {integrity: sha512-akbF8+uvleHs8sejNPQxwmVFuInAg6FMNHOwMILXfP518YfFJwdR3jr6oNUPOaEJfuEhn/vkNOCIT6ASUd4mbg==} + + '@peculiar/asn1-ecc@2.8.0': + resolution: {integrity: sha512-ohwlk+u9Rv2NOAY1c6MfHj45ATVF8R1DUN/WCgABiRtLi2ZftlZWZX7KvpAbU8v9xPcmoILfELeEABj/rn18AQ==} + + '@peculiar/asn1-pfx@2.8.0': + resolution: {integrity: sha512-5yof1ytoB++RQtaFbqSUJ8pxDJtZT6vbVqZ8XoJ61ph7UjNVvfFwAilnCodqkNsAodpy13gDhoxZXw00pghnyg==} + + '@peculiar/asn1-pkcs8@2.8.0': + resolution: {integrity: sha512-qAKXtLpBEw9LqhKpjw3ajZSXlBur+ipW+y2ivVBQAG6F6qRx94yO+1ZR4mvw+YaCfKSaOzLeYEzsPaBp4SJELA==} + + '@peculiar/asn1-pkcs9@2.8.0': + resolution: {integrity: sha512-b5nDWCnkV60+cQ141D6sVVwK9nz64R5n3zSVnklGd+ECdkW2Ol3U1a6yYFlalpSOaD557yuJB64A+q42jG7lUQ==} + + '@peculiar/asn1-rsa@2.8.0': + resolution: {integrity: sha512-zHEUlCqB2mk7x2lxDwHHJy7hWZOPdGHVlsmITWKB5/PbQo61atbu9PJ/0r9dQNMwFzbKPXZ8uK8/91eUhRznSg==} + + '@peculiar/asn1-schema@2.8.0': + resolution: {integrity: sha512-7YT0U/ze0tF2QOBbE15gKZwy5tvgGyLRiRHLzhlbOpf7BT032oBSd0haZqXn5W6l26WLlu3dyxzjM+2638/z2Q==} + + '@peculiar/asn1-x509-attr@2.8.0': + resolution: {integrity: sha512-tHjkfS/qhMnmrlB2J9NhflQlQ7In3khO3CfmVrriOlpTeErY9ZIKOso1hQ5JQiyrJ7ShvqVPk7E5fQmbclkSKA==} + + '@peculiar/asn1-x509@2.8.0': + resolution: {integrity: sha512-N0CMuhWUzsWEVq6F1q9X6+VKUnWzSW+cSVg+aPaGGwDdbFoFWTYgin5MHwXgpWd6y9COMBxnfy/Qc+Xc7F0Zwg==} + + '@peculiar/utils@2.0.3': + resolution: {integrity: sha512-+oL3HPFRIZ1St2K50lWCXiioIgSoxzz7R1J3uF6neO2yl1sgmpgY6XXJH4BdpoDkMWznQTeYF6oWNDZLCdQ4eQ==} + + '@peculiar/x509@1.14.3': + resolution: {integrity: sha512-C2Xj8FZ0uHWeCXXqX5B4/gVFQmtSkiuOolzAgutjTfseNOHT3pUjljDZsTSxXFGgio54bCzVFqmEOUrIVk8RDA==} + engines: {node: '>=20.0.0'} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -2776,323 +2510,267 @@ packages: resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} - '@playwright/test@1.60.0': - resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} + '@playwright/test@1.61.1': + resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} engines: {node: '>=18'} hasBin: true '@popperjs/core@2.11.8': resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} - '@rolldown/binding-android-arm64@1.0.0-beta.58': - resolution: {integrity: sha512-mWj5eE4Qc8TbPdGGaaLvBb9XfDPvE1EmZkJQgiGKwchkWH4oAJcRAKMTw7ZHnb1L+t7Ah41sBkAecaIsuUgsug==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-android-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-vRq9f4NzvbdZavhQbjkJBx7rRebDKYR9zHfO/Wg486+I7bSecdUapzCm5cyXoK+LHokTxgSq7A5baAXUZkIz0w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [android] - - '@rolldown/binding-darwin-arm64@1.0.0-beta.58': - resolution: {integrity: sha512-wFxUymI/5R8bH8qZFYDfAxAN9CyISEIYke+95oZPiv6EWo88aa5rskjVcCpKA532R+klFmdqjbbaD56GNmTF4Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-kFgEvkWLqt3YCgKB5re9RlIrx9bRsvyVUnaTakEpOPuLGzLpLapYxE9BufJNvPg8GjT6mB1alN4yN1NjzoeM8Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.0.0-beta.58': - resolution: {integrity: sha512-ybp3MkPj23VDV9PhtRwdU5qrGhlViWRV5BjKwO6epaSlUD5lW0WyY+roN3ZAzbma/9RrMTgZ/a/gtQq8YXOcqw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-darwin-x64@1.0.0-rc.4': - resolution: {integrity: sha512-JXmaOJGsL/+rsmMfutcDjxWM2fTaVgCHGoXS7nE8Z3c9NAYjGqHvXrAhMUZvMpHS/k7Mg+X7n/MVKb7NYWKKww==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [darwin] - - '@rolldown/binding-freebsd-x64@1.0.0-beta.58': - resolution: {integrity: sha512-Evxj3yh7FWvyklUYZa0qTVT9N2zX9TPDqGF056hl8hlCZ9/ndQ2xMv6uw9PD1VlLpukbsqL+/C6M0qwipL0QMg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': - resolution: {integrity: sha512-ep3Catd6sPnHTM0P4hNEvIv5arnDvk01PfyJIJ+J3wVCG1eEaPo09tvFqdtcaTrkwQy0VWR24uz+cb4IsK53Qw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [freebsd] - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.58': - resolution: {integrity: sha512-tYeXprDOrEgVHUbPXH6MPso4cM/c6RTkmJNICMQlYdki4hGMh92aj3yU6CKs+4X5gfG0yj5kVUw/L4M685SYag==} - engines: {node: ^20.19.0 || >=22.12.0} + '@rollup/rollup-android-arm-eabi@4.60.2': + resolution: {integrity: sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==} cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': - resolution: {integrity: sha512-LwA5ayKIpnsgXJEwWc3h8wPiS33NMIHd9BhsV92T8VetVAbGe2qXlJwNVDGHN5cOQ22R9uYvbrQir2AB+ntT2w==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm] - os: [linux] - - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.58': - resolution: {integrity: sha512-N78vmZzP6zG967Ohr+MasCjmKtis0geZ1SOVmxrA0/bklTQSzH5kHEjW5Qn+i1taFno6GEre1E40v0wuWsNOQw==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-AC1WsGdlV1MtGay/OQ4J9T7GRadVnpYRzTcygV1hKnypbYN20Yh4t6O1Sa2qRBMqv1etulUknqXjc3CTIsBu6A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.58': - resolution: {integrity: sha512-l+p4QVtG72C7wI2SIkNQw/KQtSjuYwS3rV6AKcWrRBF62ClsFUcif5vLaZIEbPrCXu5OFRXigXFJnxYsVVZqdQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-lU+6rgXXViO61B4EudxtVMXSOfiZONR29Sys5VGSetUY7X8mg9FCKIIjcPPj8xNDeYzKl+H8F/qSKOBVFJChCQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.58': - resolution: {integrity: sha512-urzJX0HrXxIh0FfxwWRjfPCMeInU9qsImLQxHBgLp5ivji1EEUnOfux8KxPPnRQthJyneBrN2LeqUix9DYrNaQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - resolution: {integrity: sha512-DZaN1f0PGp/bSvKhtw50pPsnln4T13ycDq1FrDWRiHmWt1JeW+UtYg9touPFf8yt993p8tS2QjybpzKNTxYEwg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [glibc] - - '@rolldown/binding-linux-x64-musl@1.0.0-beta.58': - resolution: {integrity: sha512-7ijfVK3GISnXIwq/1FZo+KyAUJjL3kWPJ7rViAL6MWeEBhEgRzJ0yEd9I8N9aut8Y8ab+EKFJyRNMWZuUBwQ0A==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - resolution: {integrity: sha512-RnGxwZLN7fhMMAItnD6dZ7lvy+TI7ba+2V54UF4dhaWa/p8I/ys1E73KO6HmPmgz92ZkfD8TXS1IMV8+uhbR9g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [linux] - libc: [musl] - - '@rolldown/binding-openharmony-arm64@1.0.0-beta.58': - resolution: {integrity: sha512-/m7sKZCS+cUULbzyJTIlv8JbjNohxbpAOA6cM+lgWgqVzPee3U6jpwydrib328JFN/gF9A99IZEnuGYqEDJdww==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - resolution: {integrity: sha512-6lcI79+X8klGiGd8yHuTgQRjuuJYNggmEml+RsyN596P23l/zf9FVmJ7K0KVKkFAeYEdg0iMUKyIxiV5vebDNQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [openharmony] - - '@rolldown/binding-wasm32-wasi@1.0.0-beta.58': - resolution: {integrity: sha512-6SZk7zMgv+y3wFFQ9qE5P9NnRHcRsptL1ypmudD26PDY+PvFCvfHRkJNfclWnvacVGxjowr7JOL3a9fd1wWhUw==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4': - resolution: {integrity: sha512-wz7ohsKCAIWy91blZ/1FlpPdqrsm1xpcEOQVveWoL6+aSPKL4VUcoYmmzuLTssyZxRpEwzuIxL/GDsvpjaBtOw==} - engines: {node: '>=14.0.0'} - cpu: [wasm32] - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.58': - resolution: {integrity: sha512-sFqfYPnBZ6xBhMkadB7UD0yjEDRvs7ipR3nCggblN+N4ODCXY6qhg/bKL39+W+dgQybL7ErD4EGERVbW9DAWvg==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-cfiMrfuWCIgsFmcVG0IPuO6qTRHvF7NuG3wngX1RZzc6dU8FuBFb+J3MIR5WrdTNozlumfgL4cvz+R4ozBCvsQ==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [arm64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.58': - resolution: {integrity: sha512-AnFWJdAqB8+IDPcGrATYs67Kik/6tnndNJV2jGRmwlbeNiQQ8GhRJU8ETRlINfII0pqi9k4WWLnb00p1QCxw/Q==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - resolution: {integrity: sha512-p6UeR9y7ht82AH57qwGuFYn69S6CZ7LLKdCKy/8T3zS9VTrJei2/CGsTUV45Da4Z9Rbhc7G4gyWQ/Ioamqn09g==} - engines: {node: ^20.19.0 || >=22.12.0} - cpu: [x64] - os: [win32] - - '@rolldown/pluginutils@1.0.0-beta.58': - resolution: {integrity: sha512-qWhDs6yFGR5xDfdrwiSa3CWGIHxD597uGE/A9xGqytBjANvh4rLCTTkq7szhMV4+Ygh+PMS90KVJ8xWG/TkX4w==} - - '@rolldown/pluginutils@1.0.0-rc.4': - resolution: {integrity: sha512-1BrrmTu0TWfOP1riA8uakjFc9bpIUGzVKETsOtzY39pPga8zELGDl8eu1Dx7/gjM5CAz14UknsUMpBO8L+YntQ==} + os: [android] '@rollup/rollup-android-arm-eabi@4.61.0': resolution: {integrity: sha512-dnxczajOqt0gesZlN5pGQ1s1imQVrsmCw5G2Ci4oM+0WvNz3pyRnlWrT7McoZIb8VlFwCawdmbWRmxRn7HI+VQ==} cpu: [arm] os: [android] + '@rollup/rollup-android-arm64@4.60.2': + resolution: {integrity: sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==} + cpu: [arm64] + os: [android] + '@rollup/rollup-android-arm64@4.61.0': resolution: {integrity: sha512-Bp3JpGP00Vu3f238ivRrjf7z3xSzVPXqCmaJYA9t2c+c8vKYvOzmXF7LkkeUalTEGd6cZcSWe+PFIP3Vy48fRg==} cpu: [arm64] os: [android] + '@rollup/rollup-darwin-arm64@4.60.2': + resolution: {integrity: sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==} + cpu: [arm64] + os: [darwin] + '@rollup/rollup-darwin-arm64@4.61.0': resolution: {integrity: sha512-zaYIpr670mUmmZ1tVzUFplbQbG7h3Gugx3L5FoqhsC2m/YnLlR1a7zVLmXNPy+iY1tFPEbNG+HHBXZGyId0G5w==} cpu: [arm64] os: [darwin] + '@rollup/rollup-darwin-x64@4.60.2': + resolution: {integrity: sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==} + cpu: [x64] + os: [darwin] + '@rollup/rollup-darwin-x64@4.61.0': resolution: {integrity: sha512-+P49fvkv2dSoeevUW+lgZ/I2JHSsJCK1Lyjj7Cu6E4UHG4tS9XIefzIjo5qhgELjAclnen1rLzK2PMKJdo+Dyg==} cpu: [x64] os: [darwin] + '@rollup/rollup-freebsd-arm64@4.60.2': + resolution: {integrity: sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==} + cpu: [arm64] + os: [freebsd] + '@rollup/rollup-freebsd-arm64@4.61.0': resolution: {integrity: sha512-l3FAAOyKJXH2ea6KNFN+MMgC/rnE94YGLXs2ehYqDcCoHt1DpvgWX75BhUJxN38XojP7Ul+4H8PRn7EdyqSDrw==} cpu: [arm64] os: [freebsd] + '@rollup/rollup-freebsd-x64@4.60.2': + resolution: {integrity: sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==} + cpu: [x64] + os: [freebsd] + '@rollup/rollup-freebsd-x64@4.61.0': resolution: {integrity: sha512-VokPN3TSctKj65cyCNPaUh4vMFA8awxOot/0sp+4J7ZlNRKQEhXhawqPwajoi8H5ZFt61i0ugZJuTKXBjGJ17Q==} cpu: [x64] os: [freebsd] + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + resolution: {integrity: sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==} + cpu: [arm] + os: [linux] + '@rollup/rollup-linux-arm-gnueabihf@4.61.0': resolution: {integrity: sha512-DxH0P3wxm+Yzs/p3zrk9dw1rURu8p0Nv5+MRK/L7OtnLNg5rLZraSBFZ8iUXOd9f2BlhJyEpIZUH/emjq4UJ4g==} cpu: [arm] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + resolution: {integrity: sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==} + cpu: [arm] + os: [linux] '@rollup/rollup-linux-arm-musleabihf@4.61.0': resolution: {integrity: sha512-T6ZvMNe84kAz6TBWHC7hGAoEtzP1LWYw/AqayGWEF6uISt3Abk/st06LqRD9THd7Xz3NxzurUpzAuEAUbZf+nw==} cpu: [arm] os: [linux] - libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.60.2': + resolution: {integrity: sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==} + cpu: [arm64] + os: [linux] '@rollup/rollup-linux-arm64-gnu@4.61.0': resolution: {integrity: sha512-q/4hzvQkDs8b4jIBab1pnLiiM0ayTZsN2amBFPDzuyZxjEd4wDwx0UJFYM3cOZzSf5Kw8fnWSprJzIBMkcR44Q==} cpu: [arm64] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.60.2': + resolution: {integrity: sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==} + cpu: [arm64] + os: [linux] '@rollup/rollup-linux-arm64-musl@4.61.0': resolution: {integrity: sha512-vvYWX3akdEAY6km+9wAqFDnk6pQsbJKVnj7xawcvs/+fdlYBGp+U+Qq/lLfpIxYIZvZLHMAKD9HLdacSx/r3dw==} cpu: [arm64] os: [linux] - libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.60.2': + resolution: {integrity: sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==} + cpu: [loong64] + os: [linux] '@rollup/rollup-linux-loong64-gnu@4.61.0': resolution: {integrity: sha512-DePa5cqOxDP/Zp0VOXpeWaGew5iIv5DXp9NYbzkX5PFQyWVX9184WCTh3hvr/7lhXo8ZVlbFLkz8+o/q1dU6gA==} cpu: [loong64] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.60.2': + resolution: {integrity: sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==} + cpu: [loong64] + os: [linux] '@rollup/rollup-linux-loong64-musl@4.61.0': resolution: {integrity: sha512-LV8aWMB8UChglMCEzs7RkN0GsH29RJaLLqwm9fCIjlqwxQTiWAqNcc7wjBkH31hV0PU/yVxGYvrYsgfea2qw6g==} cpu: [loong64] os: [linux] - libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + resolution: {integrity: sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==} + cpu: [ppc64] + os: [linux] '@rollup/rollup-linux-ppc64-gnu@4.61.0': resolution: {integrity: sha512-QoNSnwQtaeNu5grdBbsL0tt1uyl5EnS8DA8Mr3nluMXbhdQNyhN+G4tBax7VCdxLKj8YJ0/4OO9Ho84jMnJtKA==} cpu: [ppc64] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.60.2': + resolution: {integrity: sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==} + cpu: [ppc64] + os: [linux] '@rollup/rollup-linux-ppc64-musl@4.61.0': resolution: {integrity: sha512-/zZp5MKapIIApE8trN8qLGNSiRN9TUoaUZ1cmVu4XnVdd5LQLOXTtyi+vtfUbNnT3iyjzpPqYeKXmvJ+gJGYWw==} cpu: [ppc64] os: [linux] - libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + resolution: {integrity: sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==} + cpu: [riscv64] + os: [linux] '@rollup/rollup-linux-riscv64-gnu@4.61.0': resolution: {integrity: sha512-RbrzcD3aJ1k3UbtMRRBNwojdVVyXjuVAFTfn/xPa6EEl6GE9Sm/akPgFTb9aAC9pMKGJ6CtWxaGrqWcabH+ySg==} cpu: [riscv64] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.60.2': + resolution: {integrity: sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==} + cpu: [riscv64] + os: [linux] '@rollup/rollup-linux-riscv64-musl@4.61.0': resolution: {integrity: sha512-ZF+onDsBso8PJf1XaG9lB+O9RnBpKGnY6OrzC4CSHrtC1jb6jWLTKK4bRqdoCXHd22gyr2hiYmEAm8Wns/BOCw==} cpu: [riscv64] os: [linux] - libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.60.2': + resolution: {integrity: sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==} + cpu: [s390x] + os: [linux] '@rollup/rollup-linux-s390x-gnu@4.61.0': resolution: {integrity: sha512-Atk0aSIk5Zx2Wuh9dgRQgLP0Koc8hOeYpbWryMXyk8G8/HmPkwPPkMqIIDhrXHHYqfUzSJA/I7IWSBv8xSmRBA==} cpu: [s390x] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.60.2': + resolution: {integrity: sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==} + cpu: [x64] + os: [linux] '@rollup/rollup-linux-x64-gnu@4.61.0': resolution: {integrity: sha512-0uMOcf3eZ5K+K4cYHkdxShFMPlPXCOdfDFEFn9dNYAEEd2cVvmOfH7zFgRVoDgmtQ1m9k5q7qfrHzyMAubKYUA==} cpu: [x64] os: [linux] - libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.60.2': + resolution: {integrity: sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==} + cpu: [x64] + os: [linux] '@rollup/rollup-linux-x64-musl@4.61.0': resolution: {integrity: sha512-mvFtE4A/t/7hRJ7X8Ozmu8FsIkAUat2nzl12pgU337BRmq87AQUJztwHz2Zv5/tjo9/C95E66CK03SI/ToEDJw==} cpu: [x64] os: [linux] - libc: [musl] + + '@rollup/rollup-openbsd-x64@4.60.2': + resolution: {integrity: sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==} + cpu: [x64] + os: [openbsd] '@rollup/rollup-openbsd-x64@4.61.0': resolution: {integrity: sha512-z9b9+aTxvt8n2rNltMPvyaUfB8NJ+CVyOrGK/MdIKHx7B+lXmZpm/XbRsU7Rpf3fRqJ2uS6mBJiJveCtq8LHDg==} cpu: [x64] os: [openbsd] + '@rollup/rollup-openharmony-arm64@4.60.2': + resolution: {integrity: sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==} + cpu: [arm64] + os: [openharmony] + '@rollup/rollup-openharmony-arm64@4.61.0': resolution: {integrity: sha512-jXaXFqKMehsOc+g8R6oo33RRC6w07G9jDBxAE5eAKX7mOcCbZloYIPNhfG9Wl+P9O9IWHFO4OJgPi1Ml2qkt7w==} cpu: [arm64] os: [openharmony] + '@rollup/rollup-win32-arm64-msvc@4.60.2': + resolution: {integrity: sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==} + cpu: [arm64] + os: [win32] + '@rollup/rollup-win32-arm64-msvc@4.61.0': resolution: {integrity: sha512-OXNWVFocS2IA4+QplhTZZ2a+8hPZR7T8KuozsNmJKK8y7cp83StHvGksfHzPG3wczWTczyWHVQuqeiTUbjiyBg==} cpu: [arm64] os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.60.2': + resolution: {integrity: sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==} + cpu: [ia32] + os: [win32] + '@rollup/rollup-win32-ia32-msvc@4.61.0': resolution: {integrity: sha512-AlAbNtBO637LxSldqV43z0FfXoGfl2TW1DgAg/bs7aQswFbDewz2SJm3BUhiGfbOVtW571xbc9p+REdxhyN/Eg==} cpu: [ia32] os: [win32] + '@rollup/rollup-win32-x64-gnu@4.60.2': + resolution: {integrity: sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-gnu@4.61.0': resolution: {integrity: sha512-QRSrQXyJ1M4tjNXdR0/G/IgV6lzfQQJYBjlWIEYkY2Xs86DRl/iEpQ4blMDjJxSl7n19eDKKXMg0AmuBVYy8pQ==} cpu: [x64] os: [win32] + '@rollup/rollup-win32-x64-msvc@4.60.2': + resolution: {integrity: sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==} + cpu: [x64] + os: [win32] + '@rollup/rollup-win32-x64-msvc@4.61.0': resolution: {integrity: sha512-tkuFxhvKO/HlGd0VsINF6vHSYH8AF8W0TcNxKDK6JZmrehngFj78pToc8iemtnvwilDjs2G/qSzYFhe9U8q+fw==} cpu: [x64] os: [win32] - '@schematics/angular@21.2.12': - resolution: {integrity: sha512-eHoAbxd6Kdw9YIQeZO/6lBXTmKKi10t4WTujY8CM5v4qv1zoJu9yiwVeQp9y3e7/Sybz5Ec3m4FmQ0Tw8iVDiA==} - 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'} + '@schematics/angular@22.0.5': + resolution: {integrity: sha512-e+1Ob5Kxt9QH2Gige0Bl56CXclG819RMl5Fso3kwO58+9AQnR2QxvjbetB7LDRl+YEG3TXCE4s3vdrV4gqRRAQ==} + engines: {node: ^22.22.3 || ^24.15.0 || >=26.0.0, npm: ^6.11.0 || ^7.5.6 || >=8.0.0, yarn: '>= 1.13.0'} '@sigstore/bundle@4.0.0': resolution: {integrity: sha512-NwCl5Y0V6Di0NexvkTqdoVfmjTaQwoLM236r89KEojGmq/jMls8S+zb7yOwAPdXvbwfKDlP+lmXgAL4vKSQT+A==} @@ -3189,6 +2867,9 @@ packages: '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -3225,11 +2906,8 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/node-forge@1.3.14': - resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - - '@types/node@25.9.1': - resolution: {integrity: sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg==} + '@types/node@26.0.0': + resolution: {integrity: sha512-vf2YFi1iY9lHGwNJMs01biZFbKJkrZR1T6/MlzjhJLPdntOHLhTrDSnSVcdtvjihi4VQNlrFRIxLsDBlQpAipA==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -3270,63 +2948,63 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typescript-eslint/eslint-plugin@8.60.0': - resolution: {integrity: sha512-QYb/sa74/s7OKMbACMjrYnGspj9Hs5YI5aaffSL65UfeBUzVzBJfVo3oWSpbzPurvm7yaCCo2Lk7lVj610HqKw==} + '@typescript-eslint/eslint-plugin@8.62.0': + resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.60.0 + '@typescript-eslint/parser': ^8.62.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.60.0': - resolution: {integrity: sha512-fcqpj/MyK4sxDPcbe7STNPbpQL4RLZOPWuaTmwZYuc+hJKzRf58yRxfhqGpc6PIq9ZyfSBpfHgmUHmHs0KwHwg==} + '@typescript-eslint/parser@8.62.0': + resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.60.0': - resolution: {integrity: sha512-aZu74NNKJeUWqCjDddzdiKaS82dgYgV/vmf+Ui3ZdZejmgfXR/q+pRumgobnQ2cCJTgGTWp4ypiwsuofFubavg==} + '@typescript-eslint/project-service@8.62.0': + resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.60.0': - resolution: {integrity: sha512-pFzqhllJMs+jghLQWzV00ds39xLzuyqPSev5pd8f4Ir0rtKR3ZLUB4/4dhjOFighWb9larvtfJvqL+4yKDI3Xw==} + '@typescript-eslint/scope-manager@8.62.0': + resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.60.0': - resolution: {integrity: sha512-BZPR3RGYlAXnly6ymAxfkVn5rCbZzQNou0rxv3GfWZ8cTQp+hhVd73khbGLAd8k1TlAPLISH337M+tAgAnaJDQ==} + '@typescript-eslint/tsconfig-utils@8.62.0': + resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.60.0': - resolution: {integrity: sha512-SX46wEUtitCpq7AN38HkUU/+zvUpdKf7ephtWAFgckH8O7PQIyL5gvrhQgBLuEYgLfuKWOVvWVskMbuFHAz5xg==} + '@typescript-eslint/type-utils@8.62.0': + resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.60.0': - resolution: {integrity: sha512-AsE7x2XaAK+CVbeih0Fvbn+r1qHxtpLDJ3XUuFcIinT318T90yHMJC+Zgv+jUuDjQQd06HKwxnDu6sz1IcTilA==} + '@typescript-eslint/types@8.62.0': + resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.60.0': - resolution: {integrity: sha512-3AcZNBGMClm6CXDyo8kYvVGT/sx29sS0oBsIb9oZI2gunA4Vm2M3YHzRLPvsUBBsl+yB5FPtltq7gGH0iTlp9g==} + '@typescript-eslint/typescript-estree@8.62.0': + resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.60.0': - resolution: {integrity: sha512-HtXuPfrHTyBDkameWpl+vJb1Uevu2tznAyahM1Oc4AENidCLTPiZDWIo4GfcxNdC/RcfGcadzzkqbRG87dUrQA==} + '@typescript-eslint/utils@8.62.0': + resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.60.0': - resolution: {integrity: sha512-9WI52t8ZGLVGrPMBet25yAftqY/n95+zmoUUtJBBQTKDSKUu7OsPTroT2op7U9JatkoRccL0YkWDNMFfC4Sjxg==} + '@typescript-eslint/visitor-keys@8.62.0': + resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ungap/structured-clone@1.3.1': @@ -3371,61 +3049,51 @@ packages: resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} cpu: [loong64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} cpu: [loong64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] - libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] - libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.12.2': resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] - libc: [musl] '@unrs/resolver-binding-openharmony-arm64@1.12.2': resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} @@ -3452,17 +3120,11 @@ packages: cpu: [x64] os: [win32] - '@vitejs/plugin-basic-ssl@2.1.0': - resolution: {integrity: sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==} + '@vitejs/plugin-basic-ssl@2.3.0': + resolution: {integrity: sha512-bdyo8rB3NnQbikdMpHaML9Z1OZPBu6fFOBo+OtxsBlvMJtysWskmBcnbIDhUqgC8tcxNv/a+BcV5U+2nQMm1OQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} peerDependencies: - vite: ^6.0.0 || ^7.0.0 - - '@vitejs/plugin-basic-ssl@2.1.4': - resolution: {integrity: sha512-HXciTXN/sDBYWgeAD4V4s0DN0g72x5mlxQhHxtYu3Tt8BLa6MzcJZUyDVFCdtjNs3bfENVHVzOsmooTVuNgAAw==} - engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} - peerDependencies: - vite: ^6.0.0 || ^7.0.0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -3550,6 +3212,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.17.0: + resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} + engines: {node: '>=0.4.0'} + hasBin: true + adjust-sourcemap-loader@4.0.0: resolution: {integrity: sha512-OXwN5b9pCUXNQHJpwwD2qP40byEmSgzj8B4ydSN0uMNYWiFmJ6x6KwUllMmfk8Rwu/HJDFR7U8ubsWBoN0Xp0A==} engines: {node: '>=8.9'} @@ -3558,6 +3225,10 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + agent-base@9.0.0: + resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} + engines: {node: '>= 20'} + ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} peerDependencies: @@ -3582,17 +3253,11 @@ packages: ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} - ajv@8.17.1: - resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} - - ajv@8.18.0: - resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} - ajv@8.20.0: resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} - algoliasearch@5.48.1: - resolution: {integrity: sha512-Rf7xmeuIo7nb6S4mp4abW2faW8DauZyE2faBIKFaUfP3wnpOvNSbiI5AwVhqBNj0jPgBWEvhyCu0sLjN2q77Rg==} + algoliasearch@5.52.0: + resolution: {integrity: sha512-0ZzY9mjqV7gop/AH8pIBiAS8giXP7WcSiUfoFYIzYAK9QC5c37E4SIVtJVBMwlURc0/uNt2o4RcNRvdHa4CJ5w==} engines: {node: '>= 14.0.0'} ansi-colors@4.1.3: @@ -3652,8 +3317,12 @@ packages: array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} - autoprefixer@10.4.23: - resolution: {integrity: sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==} + asn1js@3.0.10: + resolution: {integrity: sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==} + engines: {node: '>=12.0.0'} + + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: @@ -3669,12 +3338,18 @@ packages: peerDependencies: '@babel/core': ^7.11.0 || ^8.0.0-0 - babel-loader@10.0.0: - resolution: {integrity: sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==} + babel-loader@10.1.1: + resolution: {integrity: sha512-JwKSzk2kjIe7mgPK+/lyZ2QAaJcpahNAdM+hgR2HI8D0OJVkdj8Rl6J3kaLYki9pwF7P2iWnD8qVv80Lq1ABtg==} engines: {node: ^18.20.0 || ^20.10.0 || >=22.0.0} peerDependencies: - '@babel/core': ^7.12.0 + '@babel/core': ^7.12.0 || ^8.0.0-beta.1 + '@rspack/core': ^1.0.0 || ^2.0.0-0 webpack: '>=5.61.0' + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true babel-plugin-istanbul@7.0.1: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} @@ -3694,6 +3369,11 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-corejs3@0.14.2: + resolution: {integrity: sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-regenerator@0.6.6: resolution: {integrity: sha512-hYm+XLYRMvupxiQzrvXUj7YyvFFVfv5gI0R71AJzudg1g2AI2vyCPPIFEBjk162/wFzti3inBHo7isWFuEVS/A==} peerDependencies: @@ -3728,12 +3408,8 @@ packages: batch@0.6.1: resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - beasties@0.3.5: - resolution: {integrity: sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==} - engines: {node: '>=14.0.0'} - - beasties@0.4.1: - resolution: {integrity: sha512-2Imdcw3LznDuxAbJM26RHniOLAzE6WgrK8OuvVXCQtNBS8rsnD9zsSEa3fHl4hHpUY7BYTlrpvtPVbvu9G6neg==} + beasties@0.4.2: + resolution: {integrity: sha512-NvcGjG/7AVUAfRbvrJmHunDQS9uHnE6Q/7AkaPr8oKE8HjOlpjRG5075z/th2Tmlezk3VlaaS8+X9I1RwHJMQw==} engines: {node: '>=18.0.0'} before-after-hook@4.0.0: @@ -3774,8 +3450,8 @@ packages: brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - brace-expansion@5.0.6: - resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + brace-expansion@5.0.7: + resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -3808,6 +3484,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + bytestreamjs@2.0.1: + resolution: {integrity: sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==} + engines: {node: '>=6.0.0'} + cacache@20.0.4: resolution: {integrity: sha512-M3Lab8NPYlZU2exsL3bMVvMrMqgwCnMWfdZbK28bn3pK6APT/Te/I8hjRPNu1uwORY9a1eEQoifXbKPQMfMTOA==} engines: {node: ^20.17.0 || >=22.9.0} @@ -3980,12 +3660,13 @@ packages: resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} engines: {node: '>= 0.6'} - copy-anything@2.0.6: - resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + copy-anything@3.0.5: + resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==} + engines: {node: '>=12.13'} - copy-webpack-plugin@13.0.1: - resolution: {integrity: sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==} - engines: {node: '>= 18.12.0'} + copy-webpack-plugin@14.0.0: + resolution: {integrity: sha512-3JLW90aBGeaTLpM7mYQKpnVdgsUZRExY55giiZgLuX/xTQRUs1dOCwbBnWnvY6Q6rfZoXMNwzOQJCSZPppfqXA==} + engines: {node: '>= 20.9.0'} peerDependencies: webpack: ^5.1.0 @@ -4015,11 +3696,11 @@ packages: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} - css-loader@7.1.2: - resolution: {integrity: sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==} + css-loader@7.1.4: + resolution: {integrity: sha512-vv3J9tlOl04WjiMvHQI/9tmIrCxVrj6PFbHemBB1iihpeRbi/I4h033eoFIhwxBBqLhI0KYFS7yvynBFhIZfTw==} engines: {node: '>= 18.12.0'} peerDependencies: - '@rspack/core': 0.x || 1.x + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 webpack: ^5.27.0 peerDependenciesMeta: '@rspack/core': @@ -4219,9 +3900,6 @@ packages: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - errno@0.1.8: resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} hasBin: true @@ -4244,18 +3922,8 @@ packages: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} - esbuild-wasm@0.27.2: - resolution: {integrity: sha512-eUTnl8eh+v8UZIZh4MrMOKDAc8Lm7+NqP3pyuTORGFY1s/o9WoiJgKnwXy+te2J3hX7iRbFSHEyig7GsPeeJyw==} - engines: {node: '>=18'} - hasBin: true - - esbuild-wasm@0.28.0: - resolution: {integrity: sha512-5TRVKExcEmeMkccIZMzUq+Az6X2RoMAJyfl6SMMO1dMVhmvt0I2mx7gAb6zYi42n4d1ETcatFXazGKzA+aW7fg==} - engines: {node: '>=18'} - hasBin: true - - esbuild@0.27.2: - resolution: {integrity: sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==} + esbuild-wasm@0.28.1: + resolution: {integrity: sha512-p/GD4E8oYRjg3kjdKrnMb0s4PzXgJF42e0MF4H0+ACyK/kIlFRp3e0fzOleIG+wBBm6MM3XQrbpe7soEA+vJIA==} engines: {node: '>=18'} hasBin: true @@ -4264,8 +3932,8 @@ packages: engines: {node: '>=18'} hasBin: true - esbuild@0.28.0: - resolution: {integrity: sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==} + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} engines: {node: '>=18'} hasBin: true @@ -4300,8 +3968,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.4.0: - resolution: {integrity: sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==} + eslint@10.5.0: + resolution: {integrity: sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -4410,9 +4078,18 @@ packages: fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + fast-string-truncated-width@3.0.3: + resolution: {integrity: sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==} + + fast-string-width@3.0.2: + resolution: {integrity: sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==} + fast-uri@3.1.2: resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fast-wrap-ansi@0.2.2: + resolution: {integrity: sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==} + faye-websocket@0.11.4: resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} engines: {node: '>=0.8.0'} @@ -4546,6 +4223,9 @@ packages: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} @@ -4602,10 +4282,6 @@ packages: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} @@ -4673,6 +4349,10 @@ packages: resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} engines: {node: '>= 14'} + https-proxy-agent@9.0.0: + resolution: {integrity: sha512-/MVmHp58WkOypgFhCLk4fzpPcFQvTJ/e6LBI7irpIO2HfxUbpmYoHF+KzipzJpxxzJu7aJNWQ0xojJ/dzV2G5g==} + engines: {node: '>= 20'} + human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} @@ -4844,8 +4524,9 @@ packages: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} - is-what@3.14.1: - resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + is-what@4.1.16: + resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==} + engines: {node: '>=12.13'} is-wsl@3.1.0: resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} @@ -5003,17 +4684,16 @@ packages: jest-resolve: optional: true - jest-preset-angular@16.1.5: - resolution: {integrity: sha512-4YNjA8O02TAQisr3JozsyFGQ4Dkc3FQyGebjpRZfXhiMo32arYW1bXZ5KXCjSe4OoXZrFV5T5nrV7SXr4NmBuA==} - engines: {node: ^18.19.1 || ^20.11.1 || >=22.0.0} + jest-preset-angular@17.0.0: + resolution: {integrity: sha512-2yAHkA1c5rSICGJVtLYYqPC5RDsvo4+i4CwWFHVXwv41cHNX7gCWeh074IuZ5mFw7Vwsr+i25EowCnGtKkxNWw==} + engines: {node: ^20.11.1 || >=22.0.0} peerDependencies: - '@angular/compiler-cli': '>=19.0.0 <22.0.0' - '@angular/core': '>=19.0.0 <22.0.0' - '@angular/platform-browser': '>=19.0.0 <22.0.0' - '@angular/platform-browser-dynamic': '>=19.0.0 <22.0.0' + '@angular/compiler-cli': '>=20.0.0 <23.0.0' + '@angular/core': '>=20.0.0 <23.0.0' + '@angular/platform-browser': '>=20.0.0 <23.0.0' jest: ^30.0.0 jsdom: '>=26.0.0' - typescript: '>=5.5' + typescript: '>=5.8' jest-regex-util@30.0.1: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} @@ -5080,8 +4760,8 @@ packages: node-notifier: optional: true - jiti@2.6.1: - resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true jose@6.2.3: @@ -5162,11 +4842,11 @@ packages: launch-editor@2.12.0: resolution: {integrity: sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==} - less-loader@12.3.0: - resolution: {integrity: sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==} + less-loader@12.3.2: + resolution: {integrity: sha512-uLV5c702ff2jBvO7qewpkLRzkh/I9QW07ur2NKkv8TVTrtX2lrKjEbEU/LLXAn7cgpCIBbkfyUm4qYXCQs5/+w==} engines: {node: '>= 18.12.0'} peerDependencies: - '@rspack/core': 0.x || 1.x + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 less: ^3.5.0 || ^4.0.0 webpack: ^5.0.0 peerDependenciesMeta: @@ -5175,9 +4855,9 @@ packages: webpack: optional: true - less@4.4.2: - resolution: {integrity: sha512-j1n1IuTX1VQjIy3tT7cyGbX7nvQOsFLoIqobZv4ttI5axP923gA44zUj6miiA6R5Aoms4sEGVIIcucXUbRI14g==} - engines: {node: '>=14'} + less@4.6.4: + resolution: {integrity: sha512-OJmO5+HxZLLw0RLzkqaNHzcgEAQG7C0y3aMbwtCzIUFZsLMNNq/1IdAdHEycQ58CwUO3jPTHmoN+tE5I7FQxNg==} + engines: {node: '>=18'} hasBin: true leven@3.1.0: @@ -5199,16 +4879,12 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - listr2@9.0.5: - resolution: {integrity: sha512-ME4Fb83LgEgwNw96RKNvKV4VTLuXfoKudAmm2lP8Kk87KaMK0/Xrx/aAkMWmT8mDb+3MlFDspfbCs7adjRxA2g==} - engines: {node: '>=20.0.0'} + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} - lmdb@3.4.4: - resolution: {integrity: sha512-+Y2DqovevLkb6DrSQ6SXTYLEd6kvlRbhsxzgJrk7BUfOVA/mt21ak6pFDZDKxiAczHMWxrb02kXBTSTIA0O94A==} - hasBin: true - - lmdb@3.5.1: - resolution: {integrity: sha512-NYHA0MRPjvNX+vSw8Xxg6FLKxzAG+e7Pt8RqAQA/EehzHVXq9SxDqJIN3JL1hK0dweb884y8kIh6rkWvPyg9Wg==} + lmdb@3.5.4: + resolution: {integrity: sha512-9FKQA6G1MMtqNxfxvSBNXD/axeG2QRjYbNh0/ykRL5xYcRbCm2vXq7B9bhc7nSuKdHzr8/BHIwfPuYYH1UsXXw==} hasBin: true loader-runner@4.3.2: @@ -5360,8 +5036,8 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - mini-css-extract-plugin@2.9.4: - resolution: {integrity: sha512-ZWYT7ln73Hptxqxk2DxPU9MmapXRhxkJD6tkSR04dnQxm8BGu2hzgKLugK5yySD97u/8yy7Ma7E76k9ZdvtjkQ==} + mini-css-extract-plugin@2.10.2: + resolution: {integrity: sha512-AOSS0IdEB95ayVkxn5oGzNQwqAi2J0Jb/kKm43t7H73s8+f5873g0yuj0PNvK4dO75mu5DHg4nlgp4k6Kga8eg==} engines: {node: '>= 12.13.0'} peerDependencies: webpack: ^5.0.0 @@ -5448,14 +5124,9 @@ packages: resolution: {integrity: sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==} hasBin: true - mute-stream@2.0.0: - resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} - engines: {node: ^18.17.0 || >=20.5.0} - - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} nanoid@3.3.12: resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} @@ -5506,35 +5177,35 @@ packages: '@angular/common': '>=19.0.0-0' '@angular/core': '>=19.0.0-0' - ngx-cookie-service@21.3.1: - resolution: {integrity: sha512-8VEA2W7W2W3yPXhemJoVtXxr+3WW2DNLV4OaCIKDzLdzUUxJ6SzPHMmXXa26Pg8pa+fZxHK1hZfqJfUxr9RMBw==} + ngx-cookie-service@22.0.0: + resolution: {integrity: sha512-7V8am8MqTr7R1xCePfo3w+AfbunNQl+vaWPAeLtIruhsAqBXp0uunh4mYv+ikrYrdPg9pqBjckCX3ubNv8HX7w==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 - ngx-device-detector@11.0.0: - resolution: {integrity: sha512-vQ07MpUvVKgxGMPmNvbZ9mUcgH4cH0KMVCj9skMOciPh4sBUKNsorWE/FUs2eSYiLbtu7oq1JcPMcRuEWZ1rug==} + ngx-device-detector@12.0.0: + resolution: {integrity: sha512-n9mlKvClPP3El+pj7+v6/C+ywm4iq+xz2Jh5GRV3TH9dEc1yycvzh6lRCjd/d1ymLmGRHAD5cBlDIo5jfs1F7w==} peerDependencies: - '@angular/common': ^21.0.0 - '@angular/core': ^21.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 - ngx-ui-tour-core@16.0.0: - resolution: {integrity: sha512-pfIUsYwZ0x5hHZTewxHT8V/mOFUnXLM8w+3m0UlYvH0ikFMtY8WaDK4YGnAyf4ASPEx/14jtgU5VyEf9aTgcJA==} + ngx-ui-tour-core@17.0.0: + resolution: {integrity: sha512-ClXPAyNLOiNtVEibaYZZTUH/y5z6IgaJi3eRM/kNVtN/y6AGpAh2HtrTTU3v/TPvlZWShic2yjg2/jggjtL3sg==} peerDependencies: - '@angular/common': ^21.1.0 - '@angular/core': ^21.1.0 - '@angular/router': ^21.1.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@angular/router': ^22.0.0 rxjs: ^7.4.0 - ngx-ui-tour-ng-bootstrap@18.0.0: - resolution: {integrity: sha512-2yfpyRPRnxXd407yI8uU1MpZr4430JbDIgR7aRVsE7QGrC4FiRtYQdGAQx+gf/iazcpWTxK4mK9oouJB3M/acQ==} + ngx-ui-tour-ng-bootstrap@19.0.0: + resolution: {integrity: sha512-QtcYHW4e8PwN+1BA6HmM/RZ6t02IC0pcSB8Pe73Bw9N70btpmX8RLwsU9HUrbuqFG3I0KsF0IikIh3QXBk67CQ==} peerDependencies: - '@angular/common': ^21.1.0 - '@angular/core': ^21.1.0 - '@ng-bootstrap/ng-bootstrap': ^20.0.0 + '@angular/common': ^22.0.0 + '@angular/core': ^22.0.0 + '@ng-bootstrap/ng-bootstrap': ^21.0.0 - node-abi@3.92.0: - resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} + node-abi@3.93.0: + resolution: {integrity: sha512-Cu6yUpX5Iavugm8BeX7c0wgU9CvOqfd1yM6A1d2q2ZMjym7GjpASv2GdRcTq3Fx+Sb5OgBkEEpw4VnAbY6Y5RA==} engines: {node: '>=10'} node-addon-api@6.1.0: @@ -5543,10 +5214,6 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-forge@1.3.3: - resolution: {integrity: sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==} - engines: {node: '>= 6.13.0'} - node-gyp-build-optional-packages@5.2.2: resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} hasBin: true @@ -5656,12 +5323,8 @@ packages: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} - ora@9.0.0: - resolution: {integrity: sha512-m0pg2zscbYgWbqRR6ABga5c3sZdEon7bSgjnlXC64kxtxLOyjRcbbUkLj7HFyy/FTD+P2xdBWu8snGhYI0jc4A==} - engines: {node: '>=20'} - - ora@9.3.0: - resolution: {integrity: sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==} + ora@9.4.0: + resolution: {integrity: sha512-84cglkRILFxdtA8hAvLNdMrtBpPNBTrQ9/ulg0FA7xLMnD6mifv+enAIeRmvtv+WgdCE+LPGOfQmtJRrVaIVhQ==} engines: {node: '>=20'} ordered-binary@1.6.1: @@ -5698,8 +5361,8 @@ packages: package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - pacote@21.3.1: - resolution: {integrity: sha512-O0EDXi85LF4AzdjG74GUwEArhdvawi/YOHcsW6IijKNj7wm8IvEWNF5GnfuxNpQ/ZpO3L37+v8hqdVh8GgWYhg==} + pacote@21.5.1: + resolution: {integrity: sha512-KvcJ9iy3crysCsgqc4+PknH/w6jkrp8JN36mpZBPwNaDRwTfMZD37YzRazNstiZUOhuF5pno9f78n9mEJBavwg==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true @@ -5718,8 +5381,8 @@ packages: resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} engines: {node: '>= 0.10'} - parse5-html-rewriting-stream@8.0.0: - resolution: {integrity: sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==} + parse5-html-rewriting-stream@8.0.1: + resolution: {integrity: sha512-NaRku2aMpUN1Sh1Gyk1KWUh2A7EJx2c6qYzvwsPtqhoHoaURshdrceYK3LunVCm3WHhm6FS7Vcczbvdh3/UIVw==} parse5-sax-parser@8.0.0: resolution: {integrity: sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==} @@ -5763,8 +5426,8 @@ packages: path-to-regexp@8.4.2: resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} - pdfjs-dist@5.7.284: - resolution: {integrity: sha512-h4EdYQczmGhbOlqc3PPZwxevn7ApdWPbovAuWXOB/DjIyigSnwfy2oze7c6mRcSr9XgLp3eN3EeL4DyySTPMFw==} + pdfjs-dist@6.0.227: + resolution: {integrity: sha512-/P6M4SXw+70waMVLUM7rdRtvo+dEzqE1t6W/zQNvBETo2MaRa5rrvCcAYdfWGiUzadTgM0lJmRApUrW0d9zgKg==} engines: {node: '>=22.13.0 || >=24'} picocolors@1.1.1: @@ -5774,10 +5437,6 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} - engines: {node: '>=12'} - picomatch@4.0.4: resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} @@ -5790,8 +5449,8 @@ packages: resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} - piscina@5.1.4: - resolution: {integrity: sha512-7uU4ZnKeQq22t9AsmHGD2w4OYQGonwFnTypDypaWi7Qr2EvQIFVtG8J5D/3bE7W123Wdc9+v4CZDu5hJXVCtBg==} + piscina@5.2.0: + resolution: {integrity: sha512-DszUCKeVN/5G5QKo6jAVHL8fmKnkJvQ0ACiVgY7YGCq3TUB2oznAOayvZPIAdEThvhczkXR+qm3IHsNXpFCYfA==} engines: {node: '>=20.x'} pkce-challenge@5.0.1: @@ -5802,21 +5461,25 @@ packages: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} - playwright-core@1.60.0: - resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} + pkijs@3.4.0: + resolution: {integrity: sha512-emEcLuomt2j03vxD54giVB4SxTjnsqkU692xZOZXHDVoYyypEm+b3jpiTcc+Cf+myooc+/Ly0z01jqeNHVgJGw==} + engines: {node: '>=16.0.0'} + + playwright-core@1.61.1: + resolution: {integrity: sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==} engines: {node: '>=18'} hasBin: true - playwright@1.60.0: - resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} + playwright@1.61.1: + resolution: {integrity: sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==} engines: {node: '>=18'} hasBin: true - postcss-loader@8.2.0: - resolution: {integrity: sha512-tHX+RkpsXVcc7st4dSdDGliI+r4aAQDuv+v3vFYHixb6YgjreG5AG4SEB0kDK8u2s6htqEEpKlkhSBUTvWKYnA==} + postcss-loader@8.2.1: + resolution: {integrity: sha512-k98jtRzthjj3f76MYTs9JTpRqV1RaaMhEU0Lpw9OTmQZQdppg4B30VZ74BojuBHt3F4KyubHJoXCMUeM8Bqeow==} engines: {node: '>= 18.12.0'} peerDependencies: - '@rspack/core': 0.x || 1.x + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 postcss: ^7.0.0 || ^8.0.1 webpack: ^5.0.0 peerDependenciesMeta: @@ -5865,12 +5528,12 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + postcss@8.5.13: + resolution: {integrity: sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==} engines: {node: ^10 || ^12 || >=14} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} powershell-utils@0.1.0: @@ -5921,10 +5584,6 @@ packages: process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - proxy-addr@2.0.7: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} @@ -5942,13 +5601,17 @@ packages: pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.5: + resolution: {integrity: sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA==} + engines: {node: '>=16.0.0'} + qs@6.15.2: resolution: {integrity: sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==} engines: {node: '>=0.6'} - randombytes@2.1.0: - resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -6037,6 +5700,9 @@ packages: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + resolve-url-loader@5.0.0: resolution: {integrity: sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==} engines: {node: '>=12'} @@ -6050,10 +5716,6 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - retry@0.13.1: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} @@ -6061,14 +5723,9 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rolldown@1.0.0-beta.58: - resolution: {integrity: sha512-v1FCjMZCan7f+xGAHBi+mqiE4MlH7I+SXEHSQSJoMOGNNB2UYtvMiejsq9YuUOiZjNeUeV/a21nSFbrUR+4ZCQ==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - - rolldown@1.0.0-rc.4: - resolution: {integrity: sha512-V2tPDUrY3WSevrvU2E41ijZlpF+5PbZu4giH+VpNraaadsJGHa4fR6IFwsocVwEXDoAdIv5qgPPxgrvKAOIPtA==} - engines: {node: ^20.19.0 || >=22.12.0} + rollup@4.60.2: + resolution: {integrity: sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true rollup@4.61.0: @@ -6099,11 +5756,11 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sass-loader@16.0.6: - resolution: {integrity: sha512-sglGzId5gmlfxNs4gK2U3h7HlVRfx278YK6Ono5lwzuvi1jxig80YiuHkaDBVsYIKFhx8wN7XSCI0M2IDS/3qA==} + sass-loader@16.0.7: + resolution: {integrity: sha512-w6q+fRHourZ+e+xA1kcsF27iGM6jdB8teexYCfdUw0sYgcDNeZESnDNT9sUmmPm3ooziwUJXGwZJSTF3kOdBfA==} engines: {node: '>= 18.12.0'} peerDependencies: - '@rspack/core': 0.x || 1.x + '@rspack/core': 0.x || ^1.0.0 || ^2.0.0-0 node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 sass: ^1.3.0 sass-embedded: '*' @@ -6120,13 +5777,8 @@ packages: webpack: optional: true - sass@1.97.1: - resolution: {integrity: sha512-uf6HoO8fy6ClsrShvMgaKUn14f2EHQLQRtpsZZLeU/Mv0Q1K5P0+x2uvH6Cub39TVVbWNSrraUhDAoFph6vh0A==} - engines: {node: '>=14.0.0'} - hasBin: true - - sass@1.97.3: - resolution: {integrity: sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==} + sass@1.99.0: + resolution: {integrity: sha512-kgW13M54DUB7IsIRM5LvJkNlpH+WhMpooUcaWGFARkF1Tc82v9mIWkCbCYf+MBvpIUBSeSOTilpZjEPr2VYE6Q==} engines: {node: '>=14.0.0'} hasBin: true @@ -6145,9 +5797,9 @@ packages: select-hose@2.0.0: resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} - selfsigned@2.4.1: - resolution: {integrity: sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==} - engines: {node: '>=10'} + selfsigned@5.5.0: + resolution: {integrity: sha512-ftnu3TW4+3eBfLRFnDEkzGxSF/10BJBkaLJuBHZX0kiPS7bRdlpZGu6YGt4KngMkdTwJE6MbjavFpqHvqVt+Ew==} + engines: {node: '>=18'} semver@5.7.2: resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} @@ -6157,23 +5809,18 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.3: - resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} - engines: {node: '>=10'} - hasBin: true - semver@7.7.4: resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} engines: {node: '>=10'} hasBin: true - semver@7.8.1: - resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} engines: {node: '>=10'} hasBin: true - semver@7.8.4: - resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true @@ -6185,8 +5832,9 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} - serialize-javascript@6.0.2: - resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + serialize-javascript@7.0.7: + resolution: {integrity: sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g==} + engines: {node: '>=20.0.0'} serve-index@1.9.2: resolution: {integrity: sha512-KDj11HScOaLmrPxl70KYNW1PksP4Nb/CLL2yvC+Qd2kHMPEEpfc4Re2e4FOay+bC/+XQl/7zAcWON3JVo5v3KQ==} @@ -6341,10 +5989,6 @@ packages: resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} engines: {node: '>= 0.8'} - stdin-discarder@0.2.2: - resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} - engines: {node: '>=18'} - stdin-discarder@0.3.2: resolution: {integrity: sha512-eCPu1qRxPVkl5605OTWF8Wz40b4Mf45NY5LQmVPQ599knfs5QhASUm9GbJ5BDMDOXgrnh0wyEdvzmL//YMlw0A==} engines: {node: '>=18'} @@ -6383,10 +6027,6 @@ packages: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} - strip-bom@3.0.0: - resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} - engines: {node: '>=4'} - strip-bom@4.0.0: resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} engines: {node: '>=8'} @@ -6426,8 +6066,8 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - tar-fs@2.1.4: - resolution: {integrity: sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==} + tar-fs@2.1.5: + resolution: {integrity: sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==} tar-stream@2.2.0: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} @@ -6480,8 +6120,8 @@ packages: uglify-js: optional: true - terser@5.44.1: - resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} + terser@5.46.2: + resolution: {integrity: sha512-uxfo9fPcSgLDYob/w1FuL0c99MWiJDnv+5qXSQc5+Ki5NjVNsYi66INnMFBjf6uFz6OnX12piJQPF4IpjJTNTw==} engines: {node: '>=10'} hasBin: true @@ -6503,8 +6143,8 @@ packages: thunky@1.1.0: resolution: {integrity: sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==} - tinyglobby@0.2.15: - resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} engines: {node: '>=12.0.0'} tinyglobby@0.2.17: @@ -6543,10 +6183,6 @@ packages: peerDependencies: tslib: '2' - tree-kill@1.2.2: - resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} - hasBin: true - ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} @@ -6594,13 +6230,16 @@ packages: '@swc/wasm': optional: true - tsconfig-paths@4.2.0: - resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} - engines: {node: '>=6'} + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsyringe@4.10.0: + resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} + engines: {node: '>= 6.0.0'} + tuf-js@4.1.0: resolution: {integrity: sha512-50QV99kCKH5P/Vs4E2Gzp7BopNV+KzTXqWeaxrfu5IQJBOULRsTIS9seSsOVT8ZnGXzCyx55nYWAi4qJzpZKEQ==} engines: {node: ^20.17.0 || >=22.9.0} @@ -6639,8 +6278,8 @@ packages: typed-assert@1.0.9: resolution: {integrity: sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==} - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + typescript@6.0.3: + resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} engines: {node: '>=14.17'} hasBin: true @@ -6649,21 +6288,13 @@ packages: engines: {node: '>=0.8.0'} hasBin: true - undici-types@7.24.6: - resolution: {integrity: sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==} + undici-types@8.3.0: + resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} undici@6.26.0: resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} engines: {node: '>=18.17'} - undici@7.18.2: - resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} - engines: {node: '>=20.18.1'} - - undici@7.24.4: - resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} - engines: {node: '>=20.18.1'} - unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} engines: {node: '>=4'} @@ -6713,8 +6344,8 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - uuid@14.0.0: - resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} + uuid@14.0.1: + resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true uuid@8.3.2: @@ -6737,48 +6368,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@7.3.0: - resolution: {integrity: sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vite@7.3.2: - resolution: {integrity: sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==} + vite@7.3.5: + resolution: {integrity: sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -6824,10 +6415,6 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@2.5.0: - resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} - engines: {node: '>=10.13.0'} - watchpack@2.5.1: resolution: {integrity: sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==} engines: {node: '>=10.13.0'} @@ -6851,8 +6438,17 @@ packages: webpack: optional: true - webpack-dev-server@5.2.2: - resolution: {integrity: sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==} + webpack-dev-middleware@8.0.3: + resolution: {integrity: sha512-zWrde9VZDiRaFuWsjHO40wm9LxxtXEk8DdzFXdU7eU5ZpiANnZZDBbZgN3guxbEoKqUHd9YupBmynyioz42nkA==} + engines: {node: '>= 20.9.0'} + peerDependencies: + webpack: ^5.101.0 + peerDependenciesMeta: + webpack: + optional: true + + webpack-dev-server@5.2.3: + resolution: {integrity: sha512-9Gyu2F7+bg4Vv+pjbovuYDhHX+mqdqITykfzdM9UyKqKHlsE5aAjRhR+oOEfXW5vBeu8tarzlJFIZva4ZjAdrQ==} engines: {node: '>= 18.12.0'} hasBin: true peerDependencies: @@ -6885,8 +6481,8 @@ packages: webpack-virtual-modules@0.6.2: resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} - webpack@5.104.1: - resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + webpack@5.106.2: + resolution: {integrity: sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==} engines: {node: '>=10.13.0'} hasBin: true peerDependencies: @@ -6946,9 +6542,9 @@ packages: wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} - wrap-ansi@6.2.0: - resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} - engines: {node: '>=8'} + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} @@ -7042,10 +6638,6 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - yoctocolors-cjs@2.1.3: - resolution: {integrity: sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==} - engines: {node: '>=18'} - yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} @@ -7061,6 +6653,9 @@ packages: zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} + zod@4.4.2: + resolution: {integrity: sha512-IynmDyxsEsb9RKzO3J9+4SxXnl2FTFSzNBaKKaMV6tsSk0rw9gYw9gs+JFCq/qk2LCZ78KDwyj+Z289TijSkUw==} + zone.js@0.16.2: resolution: {integrity: sha512-Eky7p2Z1Ig3NnbfodSPoARCjKBSTFMnE/ACsP1L/XJEfY4SdOFce19BsUCWVwL6K5ABZFy5J3bjcMWffX+YM3Q==} @@ -7097,116 +6692,117 @@ snapshots: '@actions/io@3.0.2': {} - '@algolia/abtesting@1.14.1': + '@algolia/abtesting@1.18.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-abtesting@5.48.1': + '@algolia/client-abtesting@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-analytics@5.48.1': + '@algolia/client-analytics@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-common@5.48.1': {} + '@algolia/client-common@5.52.0': {} - '@algolia/client-insights@5.48.1': + '@algolia/client-insights@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-personalization@5.48.1': + '@algolia/client-personalization@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-query-suggestions@5.48.1': + '@algolia/client-query-suggestions@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/client-search@5.48.1': + '@algolia/client-search@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/ingestion@1.48.1': + '@algolia/ingestion@1.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/monitoring@1.48.1': + '@algolia/monitoring@1.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/recommend@5.48.1': + '@algolia/recommend@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/client-common': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 - '@algolia/requester-browser-xhr@5.48.1': + '@algolia/requester-browser-xhr@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 - '@algolia/requester-fetch@5.48.1': + '@algolia/requester-fetch@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 - '@algolia/requester-node-http@5.48.1': + '@algolia/requester-node-http@5.52.0': dependencies: - '@algolia/client-common': 5.48.1 + '@algolia/client-common': 5.52.0 '@ampproject/remapping@2.3.0': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular-builders/common@5.0.3(@types/node@25.9.1)(chokidar@5.0.0)(typescript@5.9.3)': + '@angular-builders/common@6.0.1(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - ts-node: 10.9.2(@types/node@25.9.1)(typescript@5.9.3) - tsconfig-paths: 4.2.0 + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@schematics/angular': 22.0.5(chokidar@5.0.0) + get-tsconfig: 4.14.0 + jiti: 2.7.0 transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - '@types/node' - chokidar - - typescript - '@angular-builders/custom-webpack@21.0.3(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jest-environment-jsdom@30.4.1(canvas@3.0.0))(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(jiti@2.6.1)(less@4.4.2)(postcss@8.5.15)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0)': + '@angular-builders/custom-webpack@22.0.1(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.15)(rxjs@7.8.2)(terser@5.48.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0)': dependencies: - '@angular-builders/common': 5.0.3(@types/node@25.9.1)(chokidar@5.0.0)(typescript@5.9.3) - '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) - '@angular-devkit/build-angular': 21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jest-environment-jsdom@30.4.1(canvas@3.0.0))(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(jiti@2.6.1)(typescript@5.9.3)(yaml@2.7.0) - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular/build': 21.2.12(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.15)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0) - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) + '@angular-builders/common': 6.0.1(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/build-angular': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(jiti@2.7.0)(typescript@6.0.3)(yaml@2.7.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular/build': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.15)(terser@5.48.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0) + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@schematics/angular': 22.0.5(chokidar@5.0.0) lodash: 4.18.1 + rxjs: 7.8.2 webpack-merge: 6.0.1 transitivePeerDependencies: - '@angular/compiler' @@ -7216,16 +6812,12 @@ snapshots: - '@angular/platform-server' - '@angular/service-worker' - '@angular/ssr' - - '@emnapi/core' - - '@emnapi/runtime' - '@minify-html/node' - '@rspack/core' - '@swc/core' - '@swc/css' - '@swc/html' - - '@swc/wasm' - '@types/node' - - '@web/test-runner' - browser-sync - bufferutil - chokidar @@ -7235,8 +6827,7 @@ snapshots: - debug - html-minifier-terser - html-webpack-plugin - - jest - - jest-environment-jsdom + - istanbul-lib-instrument - jiti - karma - less @@ -7244,7 +6835,6 @@ snapshots: - ng-packagr - node-sass - postcss - - protractor - sass-embedded - stylus - sugarss @@ -7260,122 +6850,103 @@ snapshots: - webpack-cli - yaml - '@angular-builders/jest@21.0.3(6a682f4f002a31c8d3b52779d7b9ab9b)': + '@angular-builders/jest@22.0.1(41a81beaf45fc81545506474f268b72a)': dependencies: - '@angular-builders/common': 5.0.3(@types/node@25.9.1)(chokidar@5.0.0)(typescript@5.9.3) - '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) - '@angular-devkit/build-angular': 21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jest-environment-jsdom@30.4.1(canvas@3.0.0))(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(jiti@2.6.1)(typescript@5.9.3)(yaml@2.7.0) - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser-dynamic': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))) - jest: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) - jest-preset-angular: 16.1.5(26662f94407112e0967a16d5ea795956) + '@angular-builders/common': 6.0.1(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/build-angular': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(jiti@2.7.0)(typescript@6.0.3)(yaml@2.7.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser-dynamic': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))) + '@schematics/angular': 22.0.5(chokidar@5.0.0) + jest: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) + jest-preset-angular: 17.0.0(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(canvas@3.0.0)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@6.0.3) lodash: 4.18.1 + rxjs: 7.8.2 transitivePeerDependencies: - '@angular/platform-browser' - '@babel/core' - '@jest/transform' - '@jest/types' - - '@swc/core' - - '@swc/wasm' - - '@types/node' - babel-jest - canvas - chokidar - jsdom - typescript - '@angular-devkit/architect@0.2101.2(chokidar@5.0.0)': + '@angular-devkit/architect@0.2200.5(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.1.2(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-devkit/architect@0.2102.12(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - rxjs: 7.8.2 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/architect@0.2102.13(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.13(chokidar@5.0.0) - rxjs: 7.8.2 - transitivePeerDependencies: - - chokidar - - '@angular-devkit/build-angular@21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jest-environment-jsdom@30.4.1(canvas@3.0.0))(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(jiti@2.6.1)(typescript@5.9.3)(yaml@2.7.0)': + '@angular-devkit/build-angular@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(jiti@2.7.0)(typescript@6.0.3)(yaml@2.7.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) - '@angular-devkit/build-webpack': 0.2101.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)))(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - '@angular-devkit/core': 21.1.2(chokidar@5.0.0) - '@angular/build': 21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0) - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.28.5 - '@babel/generator': 7.28.5 + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/build-webpack': 0.2200.5(chokidar@5.0.0)(webpack-dev-server@5.2.3(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)))(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular/build': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.13)(terser@5.46.2)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0) + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-runtime': 7.28.5(@babel/core@7.28.5) - '@babel/preset-env': 7.28.5(@babel/core@7.28.5) - '@babel/runtime': 7.28.4 - '@discoveryjs/json-ext': 0.6.3 - '@ngtools/webpack': 21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-runtime': 7.29.0(@babel/core@7.29.0) + '@babel/preset-env': 7.29.3(@babel/core@7.29.0) + '@babel/runtime': 7.29.2 + '@discoveryjs/json-ext': 1.1.0 + '@ngtools/webpack': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(typescript@6.0.3)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) ansi-colors: 4.1.3 - autoprefixer: 10.4.23(postcss@8.5.6) - babel-loader: 10.0.0(@babel/core@7.28.5)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + autoprefixer: 10.5.0(postcss@8.5.13) + babel-loader: 10.1.1(@babel/core@7.29.0)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) browserslist: 4.28.2 - copy-webpack-plugin: 13.0.1(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - css-loader: 7.1.2(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - esbuild-wasm: 0.27.2 + copy-webpack-plugin: 14.0.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + css-loader: 7.1.4(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + esbuild-wasm: 0.28.1 http-proxy-middleware: 3.0.5 istanbul-lib-instrument: 6.0.3 jsonc-parser: 3.3.1 karma-source-map-support: 1.4.0 - less: 4.4.2 - less-loader: 12.3.0(less@4.4.2)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - license-webpack-plugin: 4.0.2(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + less: 4.6.4 + less-loader: 12.3.2(less@4.6.4)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + license-webpack-plugin: 4.0.2(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) loader-utils: 3.3.1 - mini-css-extract-plugin: 2.9.4(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + mini-css-extract-plugin: 2.10.2(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) open: 11.0.0 - ora: 9.0.0 - picomatch: 4.0.3 - piscina: 5.1.4 - postcss: 8.5.6 - postcss-loader: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + ora: 9.4.0 + picomatch: 4.0.4 + piscina: 5.2.0 + postcss: 8.5.13 + postcss-loader: 8.2.1(postcss@8.5.13)(typescript@6.0.3)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) resolve-url-loader: 5.0.0 rxjs: 7.8.2 - sass: 1.97.1 - sass-loader: 16.0.6(sass@1.97.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - semver: 7.7.3 - source-map-loader: 5.0.0(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + sass: 1.99.0 + sass-loader: 16.0.7(sass@1.99.0)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + semver: 7.7.4 + source-map-loader: 5.0.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) source-map-support: 0.5.21 - terser: 5.44.1 - tinyglobby: 0.2.15 - tree-kill: 1.2.2 + terser: 5.46.2 + tinyglobby: 0.2.16 tslib: 2.8.1 - typescript: 5.9.3 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) - webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) - webpack-dev-server: 5.2.2(tslib@2.8.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + typescript: 6.0.3 + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) + webpack-dev-middleware: 8.0.3(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) + webpack-dev-server: 5.2.3(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) webpack-merge: 6.0.1 - webpack-subresource-integrity: 5.1.0(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + webpack-subresource-integrity: 5.1.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) optionalDependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) - esbuild: 0.27.2 - jest: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) - jest-environment-jsdom: 30.4.1(canvas@3.0.0) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/localize': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) + esbuild: 0.28.1 transitivePeerDependencies: - '@angular/compiler' - - '@emnapi/core' - - '@emnapi/runtime' - '@minify-html/node' - '@rspack/core' - '@swc/core' @@ -7404,30 +6975,19 @@ snapshots: - webpack-cli - yaml - '@angular-devkit/build-webpack@0.2101.2(chokidar@5.0.0)(webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)))(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6))': + '@angular-devkit/build-webpack@0.2200.5(chokidar@5.0.0)(webpack-dev-server@5.2.3(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)))(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13))': dependencies: - '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) rxjs: 7.8.2 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) - webpack-dev-server: 5.2.2(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) + webpack-dev-server: 5.2.3(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)) transitivePeerDependencies: - chokidar - '@angular-devkit/core@21.1.2(chokidar@5.0.0)': + '@angular-devkit/core@22.0.5(chokidar@5.0.0)': dependencies: - 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: 5.0.0 - - '@angular-devkit/core@21.2.12(chokidar@5.0.0)': - dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) jsonc-parser: 3.3.1 picomatch: 4.0.4 rxjs: 7.8.2 @@ -7435,69 +6995,58 @@ snapshots: optionalDependencies: chokidar: 5.0.0 - '@angular-devkit/core@21.2.13(chokidar@5.0.0)': + '@angular-devkit/schematics@22.0.5(chokidar@5.0.0)': dependencies: - ajv: 8.18.0 - ajv-formats: 3.0.1(ajv@8.18.0) - jsonc-parser: 3.3.1 - picomatch: 4.0.4 - rxjs: 7.8.2 - source-map: 0.7.6 - optionalDependencies: - chokidar: 5.0.0 - - '@angular-devkit/schematics@21.2.12(chokidar@5.0.0)': - dependencies: - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) jsonc-parser: 3.3.1 magic-string: 0.30.21 - ora: 9.3.0 + ora: 9.4.0 rxjs: 7.8.2 transitivePeerDependencies: - chokidar - '@angular-eslint/builder@21.4.0(@angular/cli@21.2.12(@types/node@25.9.1)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/builder@22.0.0(@angular/cli@22.0.5(@types/node@26.0.0)(chokidar@5.0.0))(chokidar@5.0.0)(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-devkit/architect': 0.2102.13(chokidar@5.0.0) - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular/cli': 21.2.12(@types/node@25.9.1)(chokidar@5.0.0) - eslint: 10.4.0(jiti@2.6.1) - typescript: 5.9.3 + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular/cli': 22.0.5(@types/node@26.0.0)(chokidar@5.0.0) + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - chokidar - '@angular-eslint/bundled-angular-compiler@21.4.0': {} + '@angular-eslint/bundled-angular-compiler@22.0.0': {} - '@angular-eslint/eslint-plugin-template@21.4.0(@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/eslint-plugin-template@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@angular-eslint/template-parser': 21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@angular-eslint/utils': 21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@angular-eslint/template-parser': 22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) aria-query: 5.3.2 axobject-query: 4.1.0 - eslint: 10.4.0(jiti@2.6.1) - typescript: 5.9.3 + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 - '@angular-eslint/eslint-plugin@21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/eslint-plugin@22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@angular-eslint/utils': 21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.4.0(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@angular-eslint/utils': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 - '@angular-eslint/schematics@21.4.0(@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(@angular/cli@21.2.12(@types/node@25.9.1)(chokidar@5.0.0))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(chokidar@5.0.0)(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/schematics@22.0.0(@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@angular/cli@22.0.5(@types/node@26.0.0)(chokidar@5.0.0))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(chokidar@5.0.0)(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.12(chokidar@5.0.0) - '@angular-eslint/eslint-plugin': 21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@angular-eslint/eslint-plugin-template': 21.4.0(@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(@typescript-eslint/types@8.60.0)(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@angular/cli': 21.2.12(@types/node@25.9.1)(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@angular-eslint/eslint-plugin': 22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@angular-eslint/eslint-plugin-template': 22.0.0(@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(@typescript-eslint/types@8.62.0)(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@angular/cli': 22.0.5(@types/node@26.0.0)(chokidar@5.0.0) ignore: 7.0.5 - semver: 7.7.4 + semver: 7.8.0 strip-json-comments: 3.1.1 transitivePeerDependencies: - '@angular-eslint/template-parser' @@ -7507,118 +7056,60 @@ snapshots: - eslint - typescript - '@angular-eslint/template-parser@21.4.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/template-parser@22.0.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - eslint: 10.4.0(jiti@2.6.1) + '@angular-eslint/bundled-angular-compiler': 22.0.0 + eslint: 10.5.0(jiti@2.7.0) eslint-scope: 9.1.2 - typescript: 5.9.3 + typescript: 6.0.3 - '@angular-eslint/utils@21.4.0(@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@angular-eslint/utils@22.0.0(@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@angular-eslint/bundled-angular-compiler': 21.4.0 - '@typescript-eslint/utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - eslint: 10.4.0(jiti@2.6.1) - typescript: 5.9.3 + '@angular-eslint/bundled-angular-compiler': 22.0.0 + '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 - '@angular/build@21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.6)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0)': + '@angular/build@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.13)(terser@5.46.2)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0)': dependencies: '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2101.2(chokidar@5.0.0) - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.28.5 - '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@vitejs/plugin-basic-ssl': 2.1.0(vite@7.3.0(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.7.0)) - beasties: 0.3.5 - browserslist: 4.28.2 - esbuild: 0.27.2 - https-proxy-agent: 7.0.6 - istanbul-lib-instrument: 6.0.3 - jsonc-parser: 3.3.1 - listr2: 9.0.5 - magic-string: 0.30.21 - mrmime: 2.0.1 - parse5-html-rewriting-stream: 8.0.0 - picomatch: 4.0.3 - piscina: 5.1.4 - rolldown: 1.0.0-beta.58(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - sass: 1.97.1 - semver: 7.7.3 - source-map-support: 0.5.21 - tinyglobby: 0.2.15 - tslib: 2.8.1 - typescript: 5.9.3 - undici: 7.18.2 - vite: 7.3.0(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.7.0) - watchpack: 2.5.0 - optionalDependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) - less: 4.4.2 - lmdb: 3.4.4 - postcss: 8.5.6 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - '@types/node' - - chokidar - - jiti - - lightningcss - - sass-embedded - - stylus - - sugarss - - supports-color - - terser - - tsx - - yaml - - '@angular/build@21.2.12(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)(@types/node@25.9.1)(chokidar@5.0.0)(jiti@2.6.1)(less@4.4.2)(postcss@8.5.15)(terser@5.44.1)(tslib@2.8.1)(typescript@5.9.3)(yaml@2.7.0)': - dependencies: - '@ampproject/remapping': 2.3.0 - '@angular-devkit/architect': 0.2102.12(chokidar@5.0.0) - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular/compiler': 22.0.5 + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-split-export-declaration': 7.24.7 - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@vitejs/plugin-basic-ssl': 2.1.4(vite@7.3.2(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.44.1)(yaml@2.7.0)) - beasties: 0.4.1 + '@inquirer/confirm': 6.0.12(@types/node@26.0.0) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0)) + beasties: 0.4.2 browserslist: 4.28.2 - esbuild: 0.27.3 - https-proxy-agent: 7.0.6 - istanbul-lib-instrument: 6.0.3 + esbuild: 0.28.1 + https-proxy-agent: 9.0.0 jsonc-parser: 3.3.1 - listr2: 9.0.5 + listr2: 10.2.1 magic-string: 0.30.21 mrmime: 2.0.1 - parse5-html-rewriting-stream: 8.0.0 + parse5-html-rewriting-stream: 8.0.1 picomatch: 4.0.4 - piscina: 5.1.4 - rolldown: 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - sass: 1.97.3 + piscina: 5.2.0 + rollup: 4.60.2 + sass: 1.99.0 semver: 7.7.4 source-map-support: 0.5.21 - tinyglobby: 0.2.15 + tinyglobby: 0.2.16 tslib: 2.8.1 - typescript: 5.9.3 - undici: 7.24.4 - vite: 7.3.2(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.44.1)(yaml@2.7.0) + typescript: 6.0.3 + vite: 7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.46.2)(yaml@2.7.0) watchpack: 2.5.1 optionalDependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) - less: 4.4.2 - lmdb: 3.5.1 - postcss: 8.5.15 + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/localize': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) + istanbul-lib-instrument: 6.0.3 + less: 4.6.4 + lmdb: 3.5.4 + postcss: 8.5.13 transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - '@types/node' - chokidar - jiti @@ -7631,114 +7122,167 @@ snapshots: - tsx - yaml - '@angular/cdk@21.2.12(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/build@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@types/node@26.0.0)(chokidar@5.0.0)(istanbul-lib-instrument@6.0.3)(jiti@2.7.0)(less@4.6.4)(postcss@8.5.15)(terser@5.48.0)(tslib@2.8.1)(typescript@6.0.3)(yaml@2.7.0)': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + '@ampproject/remapping': 2.3.0 + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular/compiler': 22.0.5 + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@babel/core': 7.29.0 + '@babel/helper-annotate-as-pure': 7.27.3 + '@babel/helper-split-export-declaration': 7.24.7 + '@inquirer/confirm': 6.0.12(@types/node@26.0.0) + '@vitejs/plugin-basic-ssl': 2.3.0(vite@7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0)) + beasties: 0.4.2 + browserslist: 4.28.2 + esbuild: 0.28.1 + https-proxy-agent: 9.0.0 + jsonc-parser: 3.3.1 + listr2: 10.2.1 + magic-string: 0.30.21 + mrmime: 2.0.1 + parse5-html-rewriting-stream: 8.0.1 + picomatch: 4.0.4 + piscina: 5.2.0 + rollup: 4.60.2 + sass: 1.99.0 + semver: 7.7.4 + source-map-support: 0.5.21 + tinyglobby: 0.2.16 + tslib: 2.8.1 + typescript: 6.0.3 + vite: 7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0) + watchpack: 2.5.1 + optionalDependencies: + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/localize': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) + istanbul-lib-instrument: 6.0.3 + less: 4.6.4 + lmdb: 3.5.4 + postcss: 8.5.15 + transitivePeerDependencies: + - '@types/node' + - chokidar + - jiti + - lightningcss + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + '@angular/cdk@22.0.3(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + dependencies: + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) parse5: 8.0.1 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/cli@21.2.12(@types/node@25.9.1)(chokidar@5.0.0)': + '@angular/cli@22.0.5(@types/node@26.0.0)(chokidar@5.0.0)': dependencies: - '@angular-devkit/architect': 0.2102.12(chokidar@5.0.0) - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.12(chokidar@5.0.0) - '@inquirer/prompts': 7.10.1(@types/node@25.9.1) - '@listr2/prompt-adapter-inquirer': 3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5) - '@modelcontextprotocol/sdk': 1.26.0(zod@4.3.6) - '@schematics/angular': 21.2.12(chokidar@5.0.0) + '@angular-devkit/architect': 0.2200.5(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) + '@inquirer/prompts': 8.4.2(@types/node@26.0.0) + '@listr2/prompt-adapter-inquirer': 4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.0))(@types/node@26.0.0)(listr2@10.2.1) + '@modelcontextprotocol/sdk': 1.29.0(zod@4.4.2) + '@schematics/angular': 22.0.5(chokidar@5.0.0) '@yarnpkg/lockfile': 1.1.0 - algoliasearch: 5.48.1 + algoliasearch: 5.52.0 ini: 6.0.0 jsonc-parser: 3.3.1 - listr2: 9.0.5 + listr2: 10.2.1 npm-package-arg: 13.0.2 - pacote: 21.3.1 - parse5-html-rewriting-stream: 8.0.0 + pacote: 21.5.1 + parse5-html-rewriting-stream: 8.0.1 semver: 7.7.4 yargs: 18.0.0 - zod: 4.3.6 + zod: 4.4.2 transitivePeerDependencies: - '@cfworker/json-schema' - '@types/node' - chokidar - supports-color - '@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': + '@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3)': + '@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3)': dependencies: - '@angular/compiler': 21.2.17 - '@babel/core': 7.29.0 + '@angular/compiler': 22.0.5 + '@babel/core': 7.29.7 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 5.0.0 convert-source-map: 1.9.0 reflect-metadata: 0.2.2 - semver: 7.8.1 + semver: 7.8.5 tslib: 2.8.1 yargs: 18.0.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@angular/compiler@21.2.17': + '@angular/compiler@22.0.5': dependencies: tslib: 2.8.1 - '@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)': + '@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.2.17 + '@angular/compiler': 22.0.5 zone.js: 0.16.2 - '@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) '@standard-schema/spec': 1.1.0 rxjs: 7.8.2 tslib: 2.8.1 + zod: 4.3.6 - '@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17)': + '@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5)': dependencies: - '@angular/compiler': 21.2.17 - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - '@babel/core': 7.29.0 + '@angular/compiler': 22.0.5 + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@babel/core': 7.29.7 '@types/babel__core': 7.20.5 tinyglobby: 0.2.17 yargs: 18.0.0 transitivePeerDependencies: - supports-color - '@angular/platform-browser-dynamic@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))': + '@angular/platform-browser-dynamic@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@22.0.5)(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/compiler': 21.2.17 - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/compiler': 22.0.5 + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) tslib: 2.8.1 - '@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))': + '@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) tslib: 2.8.1 - '@angular/router@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': + '@angular/router@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) rxjs: 7.8.2 tslib: 2.8.1 @@ -7756,30 +7300,8 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.29.0': {} - '@babel/compat-data@7.29.7': {} - '@babel/core@7.28.5': - dependencies: - '@babel/code-frame': 7.29.7 - '@babel/generator': 7.28.5 - '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) - '@babel/helpers': 7.29.7 - '@babel/parser': 7.29.7 - '@babel/template': 7.29.7 - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - '@jridgewell/remapping': 2.3.5 - convert-source-map: 2.0.0 - debug: 4.4.3 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - '@babel/core@7.29.0': dependencies: '@babel/code-frame': 7.29.7 @@ -7820,7 +7342,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.5': + '@babel/generator@7.29.1': dependencies: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 @@ -7848,31 +7370,31 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.28.5)': + '@babel/helper-create-class-features-plugin@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 '@babel/traverse': 7.29.7 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.5)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.4.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.6(@babel/core@7.28.5)': + '@babel/helper-define-polyfill-provider@0.6.8(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 debug: 4.4.3 lodash.debounce: 4.0.8 resolve: 1.22.11 @@ -7888,13 +7410,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-imports@7.28.6': - dependencies: - '@babel/traverse': 7.29.7 - '@babel/types': 7.29.7 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-imports@7.29.7': dependencies: '@babel/traverse': 7.29.7 @@ -7902,15 +7417,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.28.5)': - dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.29.7 - '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.7 - transitivePeerDependencies: - - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.0)': dependencies: '@babel/core': 7.29.0 @@ -7933,29 +7439,27 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/helper-plugin-utils@7.28.6': {} - '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.5)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.6 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.28.6(@babel/core@7.28.5)': + '@babel/helper-replace-supers@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-skip-transparent-expression-wrappers@7.27.1': + '@babel/helper-skip-transparent-expression-wrappers@7.29.7': dependencies: '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 @@ -7970,8 +7474,6 @@ snapshots: '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-validator-option@7.29.7': {} '@babel/helper-wrap-function@7.28.6': @@ -7991,44 +7493,52 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array@7.29.7(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': dependencies: @@ -8050,15 +7560,15 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-syntax-import-assertions@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': dependencies: @@ -8125,441 +7635,442 @@ snapshots: '@babel/core': 7.29.7 '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.5)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.5)': + '@babel/plugin-transform-async-generator-functions@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-async-to-generator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-block-scoping@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-class-properties@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-class-static-block@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-classes@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-globals': 7.29.7 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-computed-properties@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/template': 7.29.7 - '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-dotall-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-explicit-resource-management@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-exponentiation-operator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-plugin-utils': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-json-strings@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-logical-assignment-operators@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-commonjs@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.28.5)': + '@babel/plugin-transform-named-capturing-groups-regex@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-nullish-coalescing-operator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-numeric-separator@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-object-rest-spread@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-replace-supers': 7.28.6(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-replace-supers': 7.28.6(@babel/core@7.29.0) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-optional-catch-binding@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-optional-chaining@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.5)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-private-methods@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-private-property-in-object@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/helper-create-class-features-plugin': 7.28.6(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.28.5)': + '@babel/plugin-transform-regenerator@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-regexp-modifiers@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-runtime@7.28.5(@babel/core@7.28.5)': + '@babel/plugin-transform-runtime@7.29.0(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-module-imports': 7.28.6 - '@babel/helper-plugin-utils': 7.28.6 - babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.5) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) - babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.29.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-spread@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-spread@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-skip-transparent-expression-wrappers': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-property-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.28.5)': + '@babel/plugin-transform-unicode-sets-regex@7.28.6(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.5) - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.29.0) + '@babel/helper-plugin-utils': 7.29.7 - '@babel/preset-env@7.28.5(@babel/core@7.28.5)': + '@babel/preset-env@7.29.3(@babel/core@7.29.0)': dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.28.5 + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.0 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-plugin-utils': 7.28.6 - '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.5) - '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-syntax-import-attributes': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.5) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.5) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.5) - '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.28.5) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.28.5) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.5) - '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.28.5) - '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.5) - '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.28.5) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.5) - babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.28.5) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.5) - babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.28.5) + '@babel/helper-plugin-utils': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-safari-rest-destructuring-rhs-array': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.29.0) + '@babel/plugin-syntax-import-assertions': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.0) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.29.0) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-async-generator-functions': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-async-to-generator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-block-scoping': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-class-static-block': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-classes': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-computed-properties': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.29.0) + '@babel/plugin-transform-dotall-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-explicit-resource-management': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-exponentiation-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-json-strings': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-logical-assignment-operators': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-modules-commonjs': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-modules-systemjs': 7.29.4(@babel/core@7.29.0) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-named-capturing-groups-regex': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-nullish-coalescing-operator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-numeric-separator': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-rest-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-optional-catch-binding': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-optional-chaining': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.29.0) + '@babel/plugin-transform-private-methods': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-private-property-in-object': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-regenerator': 7.29.0(@babel/core@7.29.0) + '@babel/plugin-transform-regexp-modifiers': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-spread': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-property-regex': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-unicode-sets-regex': 7.28.6(@babel/core@7.29.0) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.29.0) + babel-plugin-polyfill-corejs2: 0.4.15(@babel/core@7.29.0) + babel-plugin-polyfill-corejs3: 0.14.2(@babel/core@7.29.0) + babel-plugin-polyfill-regenerator: 0.6.6(@babel/core@7.29.0) core-js-compat: 3.48.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.5)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.29.0)': dependencies: - '@babel/core': 7.28.5 - '@babel/helper-plugin-utils': 7.28.6 + '@babel/core': 7.29.0 + '@babel/helper-plugin-utils': 7.29.7 '@babel/types': 7.29.7 esutils: 2.0.3 - '@babel/runtime@7.28.4': {} + '@babel/runtime@7.29.2': {} '@babel/template@7.29.7': dependencies: @@ -8591,7 +8102,7 @@ snapshots: '@actions/core': 3.0.1 '@actions/github': 9.1.1 chalk: 4.1.2 - semver: 7.8.1 + semver: 7.8.5 unplugin: 1.16.1 zod: 3.25.76 @@ -8627,7 +8138,7 @@ snapshots: '@ctrl/tinycolor@4.2.0': {} - '@discoveryjs/json-ext@0.6.3': {} + '@discoveryjs/json-ext@1.1.0': {} '@emnapi/core@1.10.0': dependencies: @@ -8645,243 +8156,165 @@ snapshots: tslib: 2.8.1 optional: true - '@esbuild/aix-ppc64@0.27.2': - optional: true - '@esbuild/aix-ppc64@0.27.3': optional: true - '@esbuild/aix-ppc64@0.28.0': - optional: true - - '@esbuild/android-arm64@0.27.2': + '@esbuild/aix-ppc64@0.28.1': optional: true '@esbuild/android-arm64@0.27.3': optional: true - '@esbuild/android-arm64@0.28.0': - optional: true - - '@esbuild/android-arm@0.27.2': + '@esbuild/android-arm64@0.28.1': optional: true '@esbuild/android-arm@0.27.3': optional: true - '@esbuild/android-arm@0.28.0': - optional: true - - '@esbuild/android-x64@0.27.2': + '@esbuild/android-arm@0.28.1': optional: true '@esbuild/android-x64@0.27.3': optional: true - '@esbuild/android-x64@0.28.0': - optional: true - - '@esbuild/darwin-arm64@0.27.2': + '@esbuild/android-x64@0.28.1': optional: true '@esbuild/darwin-arm64@0.27.3': optional: true - '@esbuild/darwin-arm64@0.28.0': - optional: true - - '@esbuild/darwin-x64@0.27.2': + '@esbuild/darwin-arm64@0.28.1': optional: true '@esbuild/darwin-x64@0.27.3': optional: true - '@esbuild/darwin-x64@0.28.0': - optional: true - - '@esbuild/freebsd-arm64@0.27.2': + '@esbuild/darwin-x64@0.28.1': optional: true '@esbuild/freebsd-arm64@0.27.3': optional: true - '@esbuild/freebsd-arm64@0.28.0': - optional: true - - '@esbuild/freebsd-x64@0.27.2': + '@esbuild/freebsd-arm64@0.28.1': optional: true '@esbuild/freebsd-x64@0.27.3': optional: true - '@esbuild/freebsd-x64@0.28.0': - optional: true - - '@esbuild/linux-arm64@0.27.2': + '@esbuild/freebsd-x64@0.28.1': optional: true '@esbuild/linux-arm64@0.27.3': optional: true - '@esbuild/linux-arm64@0.28.0': - optional: true - - '@esbuild/linux-arm@0.27.2': + '@esbuild/linux-arm64@0.28.1': optional: true '@esbuild/linux-arm@0.27.3': optional: true - '@esbuild/linux-arm@0.28.0': - optional: true - - '@esbuild/linux-ia32@0.27.2': + '@esbuild/linux-arm@0.28.1': optional: true '@esbuild/linux-ia32@0.27.3': optional: true - '@esbuild/linux-ia32@0.28.0': - optional: true - - '@esbuild/linux-loong64@0.27.2': + '@esbuild/linux-ia32@0.28.1': optional: true '@esbuild/linux-loong64@0.27.3': optional: true - '@esbuild/linux-loong64@0.28.0': - optional: true - - '@esbuild/linux-mips64el@0.27.2': + '@esbuild/linux-loong64@0.28.1': optional: true '@esbuild/linux-mips64el@0.27.3': optional: true - '@esbuild/linux-mips64el@0.28.0': - optional: true - - '@esbuild/linux-ppc64@0.27.2': + '@esbuild/linux-mips64el@0.28.1': optional: true '@esbuild/linux-ppc64@0.27.3': optional: true - '@esbuild/linux-ppc64@0.28.0': - optional: true - - '@esbuild/linux-riscv64@0.27.2': + '@esbuild/linux-ppc64@0.28.1': optional: true '@esbuild/linux-riscv64@0.27.3': optional: true - '@esbuild/linux-riscv64@0.28.0': - optional: true - - '@esbuild/linux-s390x@0.27.2': + '@esbuild/linux-riscv64@0.28.1': optional: true '@esbuild/linux-s390x@0.27.3': optional: true - '@esbuild/linux-s390x@0.28.0': - optional: true - - '@esbuild/linux-x64@0.27.2': + '@esbuild/linux-s390x@0.28.1': optional: true '@esbuild/linux-x64@0.27.3': optional: true - '@esbuild/linux-x64@0.28.0': - optional: true - - '@esbuild/netbsd-arm64@0.27.2': + '@esbuild/linux-x64@0.28.1': optional: true '@esbuild/netbsd-arm64@0.27.3': optional: true - '@esbuild/netbsd-arm64@0.28.0': - optional: true - - '@esbuild/netbsd-x64@0.27.2': + '@esbuild/netbsd-arm64@0.28.1': optional: true '@esbuild/netbsd-x64@0.27.3': optional: true - '@esbuild/netbsd-x64@0.28.0': - optional: true - - '@esbuild/openbsd-arm64@0.27.2': + '@esbuild/netbsd-x64@0.28.1': optional: true '@esbuild/openbsd-arm64@0.27.3': optional: true - '@esbuild/openbsd-arm64@0.28.0': - optional: true - - '@esbuild/openbsd-x64@0.27.2': + '@esbuild/openbsd-arm64@0.28.1': optional: true '@esbuild/openbsd-x64@0.27.3': optional: true - '@esbuild/openbsd-x64@0.28.0': - optional: true - - '@esbuild/openharmony-arm64@0.27.2': + '@esbuild/openbsd-x64@0.28.1': optional: true '@esbuild/openharmony-arm64@0.27.3': optional: true - '@esbuild/openharmony-arm64@0.28.0': - optional: true - - '@esbuild/sunos-x64@0.27.2': + '@esbuild/openharmony-arm64@0.28.1': optional: true '@esbuild/sunos-x64@0.27.3': optional: true - '@esbuild/sunos-x64@0.28.0': - optional: true - - '@esbuild/win32-arm64@0.27.2': + '@esbuild/sunos-x64@0.28.1': optional: true '@esbuild/win32-arm64@0.27.3': optional: true - '@esbuild/win32-arm64@0.28.0': - optional: true - - '@esbuild/win32-ia32@0.27.2': + '@esbuild/win32-arm64@0.28.1': optional: true '@esbuild/win32-ia32@0.27.3': optional: true - '@esbuild/win32-ia32@0.28.0': - optional: true - - '@esbuild/win32-x64@0.27.2': + '@esbuild/win32-ia32@0.28.1': optional: true '@esbuild/win32-x64@0.27.3': optional: true - '@esbuild/win32-x64@0.28.0': + '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.4.0(jiti@2.6.1))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@2.7.0))': dependencies: - eslint: 10.4.0(jiti@2.6.1) + eslint: 10.5.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -8934,130 +8367,131 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@1.0.2': {} + '@inquirer/ansi@2.0.7': {} - '@inquirer/checkbox@4.3.2(@types/node@25.9.1)': + '@inquirer/checkbox@5.2.1(@types/node@26.0.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/confirm@5.1.21(@types/node@25.9.1)': + '@inquirer/confirm@6.0.12(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/core@10.3.2(@types/node@25.9.1)': + '@inquirer/confirm@6.1.1(@types/node@26.0.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) + optionalDependencies: + '@types/node': 26.0.0 + + '@inquirer/core@11.2.1(@types/node@26.0.0)': + dependencies: + '@inquirer/ansi': 2.0.7 + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.0) cli-width: 4.1.0 - mute-stream: 2.0.0 + fast-wrap-ansi: 0.2.2 + mute-stream: 3.0.0 signal-exit: 4.1.0 - wrap-ansi: 6.2.0 - yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/editor@4.2.23(@types/node@25.9.1)': + '@inquirer/editor@5.2.2(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/external-editor': 1.0.3(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/external-editor': 3.0.3(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/expand@4.0.23(@types/node@25.9.1)': + '@inquirer/expand@5.1.1(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/external-editor@1.0.3(@types/node@25.9.1)': + '@inquirer/external-editor@3.0.3(@types/node@26.0.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/figures@1.0.15': {} + '@inquirer/figures@2.0.7': {} - '@inquirer/input@4.3.1(@types/node@25.9.1)': + '@inquirer/input@5.1.2(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/number@3.0.23(@types/node@25.9.1)': + '@inquirer/number@4.1.1(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/password@4.0.23(@types/node@25.9.1)': + '@inquirer/password@5.1.1(@types/node@26.0.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/prompts@7.10.1(@types/node@25.9.1)': + '@inquirer/prompts@8.4.2(@types/node@26.0.0)': dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@25.9.1) - '@inquirer/confirm': 5.1.21(@types/node@25.9.1) - '@inquirer/editor': 4.2.23(@types/node@25.9.1) - '@inquirer/expand': 4.0.23(@types/node@25.9.1) - '@inquirer/input': 4.3.1(@types/node@25.9.1) - '@inquirer/number': 3.0.23(@types/node@25.9.1) - '@inquirer/password': 4.0.23(@types/node@25.9.1) - '@inquirer/rawlist': 4.1.11(@types/node@25.9.1) - '@inquirer/search': 3.2.2(@types/node@25.9.1) - '@inquirer/select': 4.4.2(@types/node@25.9.1) + '@inquirer/checkbox': 5.2.1(@types/node@26.0.0) + '@inquirer/confirm': 6.1.1(@types/node@26.0.0) + '@inquirer/editor': 5.2.2(@types/node@26.0.0) + '@inquirer/expand': 5.1.1(@types/node@26.0.0) + '@inquirer/input': 5.1.2(@types/node@26.0.0) + '@inquirer/number': 4.1.1(@types/node@26.0.0) + '@inquirer/password': 5.1.1(@types/node@26.0.0) + '@inquirer/rawlist': 5.3.1(@types/node@26.0.0) + '@inquirer/search': 4.2.1(@types/node@26.0.0) + '@inquirer/select': 5.2.1(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/rawlist@4.1.11(@types/node@25.9.1)': + '@inquirer/rawlist@5.3.1(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/search@3.2.2(@types/node@25.9.1)': + '@inquirer/search@4.2.1(@types/node@26.0.0)': dependencies: - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/select@4.4.2(@types/node@25.9.1)': + '@inquirer/select@5.2.1(@types/node@26.0.0)': dependencies: - '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@25.9.1) - '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@25.9.1) - yoctocolors-cjs: 2.1.3 + '@inquirer/ansi': 2.0.7 + '@inquirer/core': 11.2.1(@types/node@26.0.0) + '@inquirer/figures': 2.0.7 + '@inquirer/type': 4.0.7(@types/node@26.0.0) optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 - '@inquirer/type@3.0.10(@types/node@25.9.1)': + '@inquirer/type@4.0.7(@types/node@26.0.0)': optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@isaacs/cliui@8.0.2': dependencies: @@ -9085,13 +8519,13 @@ snapshots: '@jest/console@30.4.1': dependencies: '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 jest-message-util: 30.4.1 jest-util: 30.4.1 slash: 3.0.0 - '@jest/core@30.4.2(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3))': + '@jest/core@30.4.2(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3))': dependencies: '@jest/console': 30.4.1 '@jest/pattern': 30.4.0 @@ -9099,7 +8533,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 4.4.0 @@ -9107,7 +8541,7 @@ snapshots: fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 jest-changed-files: 30.4.1 - jest-config: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + jest-config: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) jest-haste-map: 30.4.1 jest-message-util: 30.4.1 jest-regex-util: 30.4.0 @@ -9137,7 +8571,7 @@ snapshots: '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 '@types/jsdom': 21.1.7 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-mock: 30.4.1 jest-util: 30.4.1 jsdom: 26.1.0(canvas@3.0.0) @@ -9148,7 +8582,7 @@ snapshots: dependencies: '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-mock: 30.4.1 '@jest/expect-utils@30.0.5': @@ -9170,7 +8604,7 @@ snapshots: dependencies: '@jest/types': 30.4.1 '@sinonjs/fake-timers': 15.4.0 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-message-util: 30.4.1 jest-mock: 30.4.1 jest-util: 30.4.1 @@ -9190,12 +8624,12 @@ snapshots: '@jest/pattern@30.0.1': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-regex-util: 30.0.1 '@jest/pattern@30.4.0': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-regex-util: 30.4.0 '@jest/reporters@30.4.1': @@ -9206,7 +8640,7 @@ snapshots: '@jest/transform': 30.4.1 '@jest/types': 30.4.1 '@jridgewell/trace-mapping': 0.3.31 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 collect-v8-coverage: 1.0.3 exit-x: 0.2.2 @@ -9290,7 +8724,7 @@ snapshots: '@jest/schemas': 30.0.5 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -9300,7 +8734,7 @@ snapshots: '@jest/schemas': 30.4.1 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -9462,57 +8896,36 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.10.1(@types/node@25.9.1))(@types/node@25.9.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@4.2.3(@inquirer/prompts@8.4.2(@types/node@26.0.0))(@types/node@26.0.0)(listr2@10.2.1)': dependencies: - '@inquirer/prompts': 7.10.1(@types/node@25.9.1) - '@inquirer/type': 3.0.10(@types/node@25.9.1) - listr2: 9.0.5 + '@inquirer/prompts': 8.4.2(@types/node@26.0.0) + '@inquirer/type': 4.0.7(@types/node@26.0.0) + listr2: 10.2.1 transitivePeerDependencies: - '@types/node' - '@lmdb/lmdb-darwin-arm64@3.4.4': + '@lmdb/lmdb-darwin-arm64@3.5.4': optional: true - '@lmdb/lmdb-darwin-arm64@3.5.1': + '@lmdb/lmdb-darwin-x64@3.5.4': optional: true - '@lmdb/lmdb-darwin-x64@3.4.4': + '@lmdb/lmdb-linux-arm64@3.5.4': optional: true - '@lmdb/lmdb-darwin-x64@3.5.1': + '@lmdb/lmdb-linux-arm@3.5.4': optional: true - '@lmdb/lmdb-linux-arm64@3.4.4': + '@lmdb/lmdb-linux-x64@3.5.4': optional: true - '@lmdb/lmdb-linux-arm64@3.5.1': + '@lmdb/lmdb-win32-arm64@3.5.4': optional: true - '@lmdb/lmdb-linux-arm@3.4.4': + '@lmdb/lmdb-win32-x64@3.5.4': optional: true - '@lmdb/lmdb-linux-arm@3.5.1': - optional: true - - '@lmdb/lmdb-linux-x64@3.4.4': - optional: true - - '@lmdb/lmdb-linux-x64@3.5.1': - optional: true - - '@lmdb/lmdb-win32-arm64@3.4.4': - optional: true - - '@lmdb/lmdb-win32-arm64@3.5.1': - optional: true - - '@lmdb/lmdb-win32-x64@3.4.4': - optional: true - - '@lmdb/lmdb-win32-x64@3.5.1': - optional: true - - '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': + '@modelcontextprotocol/sdk@1.29.0(zod@4.4.2)': dependencies: '@hono/node-server': 1.19.14(hono@4.12.23) ajv: 8.20.0 @@ -9529,8 +8942,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 raw-body: 3.0.2 - zod: 4.3.6 - zod-to-json-schema: 3.25.2(zod@4.3.6) + zod: 4.4.2 + zod-to-json-schema: 3.25.2(zod@4.4.2) transitivePeerDependencies: - supports-color @@ -9552,52 +8965,52 @@ snapshots: '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4': optional: true - '@napi-rs/canvas-android-arm64@0.1.100': + '@napi-rs/canvas-android-arm64@1.0.2': optional: true - '@napi-rs/canvas-darwin-arm64@0.1.100': + '@napi-rs/canvas-darwin-arm64@1.0.2': optional: true - '@napi-rs/canvas-darwin-x64@0.1.100': + '@napi-rs/canvas-darwin-x64@1.0.2': optional: true - '@napi-rs/canvas-linux-arm-gnueabihf@0.1.100': + '@napi-rs/canvas-linux-arm-gnueabihf@1.0.2': optional: true - '@napi-rs/canvas-linux-arm64-gnu@0.1.100': + '@napi-rs/canvas-linux-arm64-gnu@1.0.2': optional: true - '@napi-rs/canvas-linux-arm64-musl@0.1.100': + '@napi-rs/canvas-linux-arm64-musl@1.0.2': optional: true - '@napi-rs/canvas-linux-riscv64-gnu@0.1.100': + '@napi-rs/canvas-linux-riscv64-gnu@1.0.2': optional: true - '@napi-rs/canvas-linux-x64-gnu@0.1.100': + '@napi-rs/canvas-linux-x64-gnu@1.0.2': optional: true - '@napi-rs/canvas-linux-x64-musl@0.1.100': + '@napi-rs/canvas-linux-x64-musl@1.0.2': optional: true - '@napi-rs/canvas-win32-arm64-msvc@0.1.100': + '@napi-rs/canvas-win32-arm64-msvc@1.0.2': optional: true - '@napi-rs/canvas-win32-x64-msvc@0.1.100': + '@napi-rs/canvas-win32-x64-msvc@1.0.2': optional: true - '@napi-rs/canvas@0.1.100': + '@napi-rs/canvas@1.0.2': optionalDependencies: - '@napi-rs/canvas-android-arm64': 0.1.100 - '@napi-rs/canvas-darwin-arm64': 0.1.100 - '@napi-rs/canvas-darwin-x64': 0.1.100 - '@napi-rs/canvas-linux-arm-gnueabihf': 0.1.100 - '@napi-rs/canvas-linux-arm64-gnu': 0.1.100 - '@napi-rs/canvas-linux-arm64-musl': 0.1.100 - '@napi-rs/canvas-linux-riscv64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-gnu': 0.1.100 - '@napi-rs/canvas-linux-x64-musl': 0.1.100 - '@napi-rs/canvas-win32-arm64-msvc': 0.1.100 - '@napi-rs/canvas-win32-x64-msvc': 0.1.100 + '@napi-rs/canvas-android-arm64': 1.0.2 + '@napi-rs/canvas-darwin-arm64': 1.0.2 + '@napi-rs/canvas-darwin-x64': 1.0.2 + '@napi-rs/canvas-linux-arm-gnueabihf': 1.0.2 + '@napi-rs/canvas-linux-arm64-gnu': 1.0.2 + '@napi-rs/canvas-linux-arm64-musl': 1.0.2 + '@napi-rs/canvas-linux-riscv64-gnu': 1.0.2 + '@napi-rs/canvas-linux-x64-gnu': 1.0.2 + '@napi-rs/canvas-linux-x64-musl': 1.0.2 + '@napi-rs/canvas-win32-arm64-msvc': 1.0.2 + '@napi-rs/canvas-win32-x64-msvc': 1.0.2 optional: true '@napi-rs/nice-android-arm-eabi@1.1.1': @@ -9679,37 +9092,39 @@ snapshots: '@tybys/wasm-util': 0.10.2 optional: true - '@ng-bootstrap/ng-bootstrap@20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2)': + '@ng-bootstrap/ng-bootstrap@21.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@popperjs/core@2.11.8)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/forms': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@angular/localize': 21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/forms': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + '@angular/localize': 22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5) '@popperjs/core': 2.11.8 rxjs: 7.8.2 tslib: 2.8.1 - '@ng-select/ng-select@21.8.2(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))': + '@ng-select/ng-select@23.2.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))': dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/forms': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/forms': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) tslib: 2.8.1 - '@ngneat/dirty-check-forms@3.0.3(ad2c8ff51b8ef8626e139c84727a024d)': - dependencies: - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/forms': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) - '@angular/router': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + ? '@ngneat/dirty-check-forms@3.0.3(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/router@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(lodash-es@4.17.21)(rxjs@7.8.2)' + : dependencies: + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/forms': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + '@angular/router': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) lodash-es: 4.17.21 rxjs: 7.8.2 tslib: 2.8.1 - '@ngtools/webpack@21.1.2(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(typescript@5.9.3)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6))': + '@ngtools/webpack@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(typescript@6.0.3)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13))': dependencies: - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - typescript: 5.9.3 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + typescript: 6.0.3 + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) + + '@noble/hashes@1.4.0': {} '@npmcli/agent@4.0.2': dependencies: @@ -9723,7 +9138,7 @@ snapshots: '@npmcli/fs@5.0.0': dependencies: - semver: 7.7.4 + semver: 7.8.5 '@npmcli/git@7.0.2': dependencies: @@ -9733,7 +9148,7 @@ snapshots: lru-cache: 11.5.1 npm-pick-manifest: 11.0.3 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 which: 6.0.1 '@npmcli/installed-package-contents@4.0.0': @@ -9750,7 +9165,7 @@ snapshots: hosted-git-info: 9.0.3 json-parse-even-better-errors: 5.0.0 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 spdx-expression-parse: 4.0.0 '@npmcli/promise-spawn@9.0.1': @@ -9819,10 +9234,6 @@ snapshots: dependencies: '@octokit/openapi-types': 27.0.0 - '@oxc-project/types@0.106.0': {} - - '@oxc-project/types@0.113.0': {} - '@parcel/watcher-android-arm64@2.5.6': optional: true @@ -9884,189 +9295,267 @@ snapshots: '@parcel/watcher-win32-x64': 2.5.6 optional: true + '@peculiar/asn1-cms@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-csr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pfx@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs8@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-pkcs9@2.8.0': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-pfx': 2.8.0 + '@peculiar/asn1-pkcs8': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + '@peculiar/asn1-x509-attr': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.8.0': + dependencies: + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509-attr@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.8.0': + dependencies: + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/utils': 2.0.3 + asn1js: 3.0.10 + tslib: 2.8.1 + + '@peculiar/utils@2.0.3': + dependencies: + tslib: 2.8.1 + + '@peculiar/x509@1.14.3': + dependencies: + '@peculiar/asn1-cms': 2.8.0 + '@peculiar/asn1-csr': 2.8.0 + '@peculiar/asn1-ecc': 2.8.0 + '@peculiar/asn1-pkcs9': 2.8.0 + '@peculiar/asn1-rsa': 2.8.0 + '@peculiar/asn1-schema': 2.8.0 + '@peculiar/asn1-x509': 2.8.0 + pvtsutils: 1.3.6 + reflect-metadata: 0.2.2 + tslib: 2.8.1 + tsyringe: 4.10.0 + '@pkgjs/parseargs@0.11.0': optional: true '@pkgr/core@0.3.6': {} - '@playwright/test@1.60.0': + '@playwright/test@1.61.1': dependencies: - playwright: 1.60.0 + playwright: 1.61.1 '@popperjs/core@2.11.8': {} - '@rolldown/binding-android-arm64@1.0.0-beta.58': + '@rollup/rollup-android-arm-eabi@4.60.2': optional: true - '@rolldown/binding-android-arm64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-darwin-arm64@1.0.0-beta.58': - optional: true - - '@rolldown/binding-darwin-arm64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-darwin-x64@1.0.0-beta.58': - optional: true - - '@rolldown/binding-darwin-x64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-freebsd-x64@1.0.0-beta.58': - optional: true - - '@rolldown/binding-freebsd-x64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.58': - optional: true - - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.58': - optional: true - - '@rolldown/binding-linux-arm64-gnu@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.58': - optional: true - - '@rolldown/binding-linux-arm64-musl@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.58': - optional: true - - '@rolldown/binding-linux-x64-gnu@1.0.0-rc.4': - optional: true - - '@rolldown/binding-linux-x64-musl@1.0.0-beta.58': - optional: true - - '@rolldown/binding-linux-x64-musl@1.0.0-rc.4': - optional: true - - '@rolldown/binding-openharmony-arm64@1.0.0-beta.58': - optional: true - - '@rolldown/binding-openharmony-arm64@1.0.0-rc.4': - optional: true - - '@rolldown/binding-wasm32-wasi@1.0.0-beta.58(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - optional: true - - '@rolldown/binding-wasm32-wasi@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': - dependencies: - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.58': - optional: true - - '@rolldown/binding-win32-arm64-msvc@1.0.0-rc.4': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.58': - optional: true - - '@rolldown/binding-win32-x64-msvc@1.0.0-rc.4': - optional: true - - '@rolldown/pluginutils@1.0.0-beta.58': {} - - '@rolldown/pluginutils@1.0.0-rc.4': {} - '@rollup/rollup-android-arm-eabi@4.61.0': optional: true + '@rollup/rollup-android-arm64@4.60.2': + optional: true + '@rollup/rollup-android-arm64@4.61.0': optional: true + '@rollup/rollup-darwin-arm64@4.60.2': + optional: true + '@rollup/rollup-darwin-arm64@4.61.0': optional: true + '@rollup/rollup-darwin-x64@4.60.2': + optional: true + '@rollup/rollup-darwin-x64@4.61.0': optional: true + '@rollup/rollup-freebsd-arm64@4.60.2': + optional: true + '@rollup/rollup-freebsd-arm64@4.61.0': optional: true + '@rollup/rollup-freebsd-x64@4.60.2': + optional: true + '@rollup/rollup-freebsd-x64@4.61.0': optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.60.2': + optional: true + '@rollup/rollup-linux-arm-gnueabihf@4.61.0': optional: true + '@rollup/rollup-linux-arm-musleabihf@4.60.2': + optional: true + '@rollup/rollup-linux-arm-musleabihf@4.61.0': optional: true + '@rollup/rollup-linux-arm64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-arm64-gnu@4.61.0': optional: true + '@rollup/rollup-linux-arm64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-arm64-musl@4.61.0': optional: true + '@rollup/rollup-linux-loong64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-loong64-gnu@4.61.0': optional: true + '@rollup/rollup-linux-loong64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-loong64-musl@4.61.0': optional: true + '@rollup/rollup-linux-ppc64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-ppc64-gnu@4.61.0': optional: true + '@rollup/rollup-linux-ppc64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-ppc64-musl@4.61.0': optional: true + '@rollup/rollup-linux-riscv64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-riscv64-gnu@4.61.0': optional: true + '@rollup/rollup-linux-riscv64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-riscv64-musl@4.61.0': optional: true + '@rollup/rollup-linux-s390x-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-s390x-gnu@4.61.0': optional: true + '@rollup/rollup-linux-x64-gnu@4.60.2': + optional: true + '@rollup/rollup-linux-x64-gnu@4.61.0': optional: true + '@rollup/rollup-linux-x64-musl@4.60.2': + optional: true + '@rollup/rollup-linux-x64-musl@4.61.0': optional: true + '@rollup/rollup-openbsd-x64@4.60.2': + optional: true + '@rollup/rollup-openbsd-x64@4.61.0': optional: true + '@rollup/rollup-openharmony-arm64@4.60.2': + optional: true + '@rollup/rollup-openharmony-arm64@4.61.0': optional: true + '@rollup/rollup-win32-arm64-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-arm64-msvc@4.61.0': optional: true + '@rollup/rollup-win32-ia32-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-ia32-msvc@4.61.0': optional: true + '@rollup/rollup-win32-x64-gnu@4.60.2': + optional: true + '@rollup/rollup-win32-x64-gnu@4.61.0': optional: true + '@rollup/rollup-win32-x64-msvc@4.60.2': + optional: true + '@rollup/rollup-win32-x64-msvc@4.61.0': optional: true - '@schematics/angular@21.2.12(chokidar@5.0.0)': + '@schematics/angular@22.0.5(chokidar@5.0.0)': dependencies: - '@angular-devkit/core': 21.2.12(chokidar@5.0.0) - '@angular-devkit/schematics': 21.2.12(chokidar@5.0.0) + '@angular-devkit/core': 22.0.5(chokidar@5.0.0) + '@angular-devkit/schematics': 22.0.5(chokidar@5.0.0) jsonc-parser: 3.3.1 + typescript: 6.0.3 transitivePeerDependencies: - chokidar @@ -10160,20 +9649,20 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/bonjour@3.5.13': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.8 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/connect@3.4.38': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/eslint-scope@3.7.7': dependencies: @@ -10187,11 +9676,13 @@ snapshots: '@types/esrecurse@4.3.1': {} + '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/express-serve-static-core@4.19.8': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 1.2.1 @@ -10207,7 +9698,7 @@ snapshots: '@types/http-proxy@1.17.17': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/istanbul-lib-coverage@2.0.6': {} @@ -10226,7 +9717,7 @@ snapshots: '@types/jsdom@21.1.7': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/tough-cookie': 4.0.5 parse5: 7.3.0 @@ -10234,13 +9725,9 @@ snapshots: '@types/mime@1.3.5': {} - '@types/node-forge@1.3.14': + '@types/node@26.0.0': dependencies: - '@types/node': 25.9.1 - - '@types/node@25.9.1': - dependencies: - undici-types: 7.24.6 + undici-types: 8.3.0 '@types/qs@6.14.0': {} @@ -10251,11 +9738,11 @@ snapshots: '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/send@1.2.1': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/serve-index@1.9.4': dependencies: @@ -10264,12 +9751,12 @@ snapshots: '@types/serve-static@1.15.10': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/send': 0.17.6 '@types/sockjs@0.3.36': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/stack-utils@2.0.3': {} @@ -10277,7 +9764,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@types/yargs-parser@21.0.3': {} @@ -10285,95 +9772,95 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@8.60.0(@typescript-eslint/parser@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3))(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.60.0 - '@typescript-eslint/type-utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.60.0 - eslint: 10.4.0(jiti@2.6.1) + '@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/type-utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.0 + eslint: 10.5.0(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.60.0 - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 - eslint: 10.4.0(jiti@2.6.1) - typescript: 5.9.3 + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.60.0(typescript@5.9.3)': + '@typescript-eslint/project-service@8.62.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) + '@typescript-eslint/types': 8.62.0 debug: 4.4.3 - typescript: 5.9.3 + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.60.0': + '@typescript-eslint/scope-manager@8.62.0': dependencies: - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 - '@typescript-eslint/tsconfig-utils@8.60.0(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - '@typescript-eslint/type-utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3) - '@typescript-eslint/utils': 8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 - eslint: 10.4.0(jiti@2.6.1) - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + eslint: 10.5.0(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.60.0': {} + '@typescript-eslint/types@8.62.0': {} - '@typescript-eslint/typescript-estree@8.60.0(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.60.0(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.60.0(typescript@5.9.3) - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/visitor-keys': 8.60.0 + '@typescript-eslint/project-service': 8.62.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/visitor-keys': 8.62.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.8.1 + semver: 7.8.5 tinyglobby: 0.2.17 - ts-api-utils: 2.5.0(typescript@5.9.3) - typescript: 5.9.3 + ts-api-utils: 2.5.0(typescript@6.0.3) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.60.0(eslint@10.4.0(jiti@2.6.1))(typescript@5.9.3)': + '@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0(jiti@2.6.1)) - '@typescript-eslint/scope-manager': 8.60.0 - '@typescript-eslint/types': 8.60.0 - '@typescript-eslint/typescript-estree': 8.60.0(typescript@5.9.3) - eslint: 10.4.0(jiti@2.6.1) - typescript: 5.9.3 + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.62.0 + '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) + eslint: 10.5.0(jiti@2.7.0) + typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.60.0': + '@typescript-eslint/visitor-keys@8.62.0': dependencies: - '@typescript-eslint/types': 8.60.0 + '@typescript-eslint/types': 8.62.0 eslint-visitor-keys: 5.0.1 '@ungap/structured-clone@1.3.1': {} @@ -10448,13 +9935,9 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.3.0(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.7.0))': + '@vitejs/plugin-basic-ssl@2.3.0(vite@7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0))': dependencies: - vite: 7.3.0(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.7.0) - - '@vitejs/plugin-basic-ssl@2.1.4(vite@7.3.2(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.44.1)(yaml@2.7.0))': - dependencies: - vite: 7.3.2(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.44.1)(yaml@2.7.0) + vite: 7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0) '@webassemblyjs/ast@1.14.1': dependencies: @@ -10554,14 +10037,20 @@ snapshots: dependencies: acorn: 8.16.0 - acorn-jsx@5.3.2(acorn@8.16.0): + acorn-import-phases@1.0.4(acorn@8.17.0): dependencies: - acorn: 8.16.0 + acorn: 8.17.0 + + acorn-jsx@5.3.2(acorn@8.17.0): + dependencies: + acorn: 8.17.0 acorn-walk@8.3.1: {} acorn@8.16.0: {} + acorn@8.17.0: {} + adjust-sourcemap-loader@4.0.0: dependencies: loader-utils: 2.0.4 @@ -10569,18 +10058,12 @@ snapshots: agent-base@7.1.4: {} + agent-base@9.0.0: {} + ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 - ajv-formats@3.0.1(ajv@8.17.1): - optionalDependencies: - ajv: 8.17.1 - - ajv-formats@3.0.1(ajv@8.18.0): - optionalDependencies: - ajv: 8.18.0 - ajv-formats@3.0.1(ajv@8.20.0): optionalDependencies: ajv: 8.20.0 @@ -10597,20 +10080,6 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 - ajv@8.17.1: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - - ajv@8.18.0: - dependencies: - fast-deep-equal: 3.1.3 - fast-uri: 3.1.2 - json-schema-traverse: 1.0.0 - require-from-string: 2.0.2 - ajv@8.20.0: dependencies: fast-deep-equal: 3.1.3 @@ -10618,22 +10087,22 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - algoliasearch@5.48.1: + algoliasearch@5.52.0: dependencies: - '@algolia/abtesting': 1.14.1 - '@algolia/client-abtesting': 5.48.1 - '@algolia/client-analytics': 5.48.1 - '@algolia/client-common': 5.48.1 - '@algolia/client-insights': 5.48.1 - '@algolia/client-personalization': 5.48.1 - '@algolia/client-query-suggestions': 5.48.1 - '@algolia/client-search': 5.48.1 - '@algolia/ingestion': 1.48.1 - '@algolia/monitoring': 1.48.1 - '@algolia/recommend': 5.48.1 - '@algolia/requester-browser-xhr': 5.48.1 - '@algolia/requester-fetch': 5.48.1 - '@algolia/requester-node-http': 5.48.1 + '@algolia/abtesting': 1.18.0 + '@algolia/client-abtesting': 5.52.0 + '@algolia/client-analytics': 5.52.0 + '@algolia/client-common': 5.52.0 + '@algolia/client-insights': 5.52.0 + '@algolia/client-personalization': 5.52.0 + '@algolia/client-query-suggestions': 5.52.0 + '@algolia/client-search': 5.52.0 + '@algolia/ingestion': 1.52.0 + '@algolia/monitoring': 1.52.0 + '@algolia/recommend': 5.52.0 + '@algolia/requester-browser-xhr': 5.52.0 + '@algolia/requester-fetch': 5.52.0 + '@algolia/requester-node-http': 5.52.0 ansi-colors@4.1.3: {} @@ -10676,13 +10145,19 @@ snapshots: array-flatten@1.1.1: {} - autoprefixer@10.4.23(postcss@8.5.6): + asn1js@3.0.10: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + autoprefixer@10.5.0(postcss@8.5.13): dependencies: browserslist: 4.28.2 caniuse-lite: 1.0.30001793 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.6 + postcss: 8.5.13 postcss-value-parser: 4.2.0 axobject-query@4.1.0: {} @@ -10700,11 +10175,12 @@ snapshots: transitivePeerDependencies: - supports-color - babel-loader@10.0.0(@babel/core@7.28.5)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + babel-loader@10.1.1(@babel/core@7.29.0)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: - '@babel/core': 7.28.5 + '@babel/core': 7.29.0 find-up: 5.0.0 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + optionalDependencies: + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) babel-plugin-istanbul@7.0.1: dependencies: @@ -10720,27 +10196,35 @@ snapshots: dependencies: '@types/babel__core': 7.20.5 - babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.28.5): + babel-plugin-polyfill-corejs2@0.4.15(@babel/core@7.29.0): dependencies: - '@babel/compat-data': 7.29.0 - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + '@babel/compat-data': 7.29.7 + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.5): + babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) core-js-compat: 3.48.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.28.5): + babel-plugin-polyfill-corejs3@0.14.2(@babel/core@7.29.0): dependencies: - '@babel/core': 7.28.5 - '@babel/helper-define-polyfill-provider': 0.6.6(@babel/core@7.28.5) + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) + core-js-compat: 3.48.0 + transitivePeerDependencies: + - supports-color + + babel-plugin-polyfill-regenerator@0.6.6(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-define-polyfill-provider': 0.6.8(@babel/core@7.29.0) transitivePeerDependencies: - supports-color @@ -10780,18 +10264,7 @@ snapshots: batch@0.6.1: {} - beasties@0.3.5: - dependencies: - css-select: 6.0.0 - css-what: 7.0.0 - dom-serializer: 2.0.0 - domhandler: 5.0.3 - htmlparser2: 10.1.0 - picocolors: 1.1.1 - postcss: 8.5.6 - postcss-media-query-parser: 0.2.3 - - beasties@0.4.1: + beasties@0.4.2: dependencies: css-select: 6.0.0 css-what: 7.0.0 @@ -10867,7 +10340,7 @@ snapshots: dependencies: balanced-match: 1.0.2 - brace-expansion@5.0.6: + brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 @@ -10905,6 +10378,8 @@ snapshots: bytes@3.1.2: {} + bytestreamjs@2.0.1: {} + cacache@20.0.4: dependencies: '@npmcli/fs': 5.0.0 @@ -11070,18 +10545,18 @@ snapshots: cookie@0.7.2: {} - copy-anything@2.0.6: + copy-anything@3.0.5: dependencies: - is-what: 3.14.1 + is-what: 4.1.16 - copy-webpack-plugin@13.0.1(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + copy-webpack-plugin@14.0.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: glob-parent: 6.0.2 normalize-path: 3.0.0 schema-utils: 4.3.3 - serialize-javascript: 6.0.2 - tinyglobby: 0.2.15 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + serialize-javascript: 7.0.7 + tinyglobby: 0.2.17 + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) core-js-compat@3.48.0: dependencies: @@ -11094,14 +10569,14 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 - cosmiconfig@9.0.0(typescript@5.9.3): + cosmiconfig@9.0.0(typescript@6.0.3): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: - typescript: 5.9.3 + typescript: 6.0.3 create-require@1.1.1: {} @@ -11111,18 +10586,18 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + css-loader@7.1.4(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 - postcss-modules-extract-imports: 3.1.0(postcss@8.5.6) - postcss-modules-local-by-default: 4.2.0(postcss@8.5.6) - postcss-modules-scope: 3.2.1(postcss@8.5.6) - postcss-modules-values: 4.0.0(postcss@8.5.6) + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 + postcss-modules-extract-imports: 3.1.0(postcss@8.5.15) + postcss-modules-local-by-default: 4.2.0(postcss@8.5.15) + postcss-modules-scope: 3.2.1(postcss@8.5.15) + postcss-modules-values: 4.0.0(postcss@8.5.15) postcss-value-parser: 4.2.0 - semver: 7.7.3 + semver: 7.8.5 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) css-select@6.0.0: dependencies: @@ -11269,8 +10744,6 @@ snapshots: environment@1.1.0: {} - err-code@2.0.3: {} - errno@0.1.8: dependencies: prr: 1.0.1 @@ -11290,38 +10763,7 @@ snapshots: dependencies: es-errors: 1.3.0 - esbuild-wasm@0.27.2: {} - - esbuild-wasm@0.28.0: {} - - esbuild@0.27.2: - optionalDependencies: - '@esbuild/aix-ppc64': 0.27.2 - '@esbuild/android-arm': 0.27.2 - '@esbuild/android-arm64': 0.27.2 - '@esbuild/android-x64': 0.27.2 - '@esbuild/darwin-arm64': 0.27.2 - '@esbuild/darwin-x64': 0.27.2 - '@esbuild/freebsd-arm64': 0.27.2 - '@esbuild/freebsd-x64': 0.27.2 - '@esbuild/linux-arm': 0.27.2 - '@esbuild/linux-arm64': 0.27.2 - '@esbuild/linux-ia32': 0.27.2 - '@esbuild/linux-loong64': 0.27.2 - '@esbuild/linux-mips64el': 0.27.2 - '@esbuild/linux-ppc64': 0.27.2 - '@esbuild/linux-riscv64': 0.27.2 - '@esbuild/linux-s390x': 0.27.2 - '@esbuild/linux-x64': 0.27.2 - '@esbuild/netbsd-arm64': 0.27.2 - '@esbuild/netbsd-x64': 0.27.2 - '@esbuild/openbsd-arm64': 0.27.2 - '@esbuild/openbsd-x64': 0.27.2 - '@esbuild/openharmony-arm64': 0.27.2 - '@esbuild/sunos-x64': 0.27.2 - '@esbuild/win32-arm64': 0.27.2 - '@esbuild/win32-ia32': 0.27.2 - '@esbuild/win32-x64': 0.27.2 + esbuild-wasm@0.28.1: {} esbuild@0.27.3: optionalDependencies: @@ -11352,35 +10794,34 @@ snapshots: '@esbuild/win32-ia32': 0.27.3 '@esbuild/win32-x64': 0.27.3 - esbuild@0.28.0: + esbuild@0.28.1: optionalDependencies: - '@esbuild/aix-ppc64': 0.28.0 - '@esbuild/android-arm': 0.28.0 - '@esbuild/android-arm64': 0.28.0 - '@esbuild/android-x64': 0.28.0 - '@esbuild/darwin-arm64': 0.28.0 - '@esbuild/darwin-x64': 0.28.0 - '@esbuild/freebsd-arm64': 0.28.0 - '@esbuild/freebsd-x64': 0.28.0 - '@esbuild/linux-arm': 0.28.0 - '@esbuild/linux-arm64': 0.28.0 - '@esbuild/linux-ia32': 0.28.0 - '@esbuild/linux-loong64': 0.28.0 - '@esbuild/linux-mips64el': 0.28.0 - '@esbuild/linux-ppc64': 0.28.0 - '@esbuild/linux-riscv64': 0.28.0 - '@esbuild/linux-s390x': 0.28.0 - '@esbuild/linux-x64': 0.28.0 - '@esbuild/netbsd-arm64': 0.28.0 - '@esbuild/netbsd-x64': 0.28.0 - '@esbuild/openbsd-arm64': 0.28.0 - '@esbuild/openbsd-x64': 0.28.0 - '@esbuild/openharmony-arm64': 0.28.0 - '@esbuild/sunos-x64': 0.28.0 - '@esbuild/win32-arm64': 0.28.0 - '@esbuild/win32-ia32': 0.28.0 - '@esbuild/win32-x64': 0.28.0 - optional: true + '@esbuild/aix-ppc64': 0.28.1 + '@esbuild/android-arm': 0.28.1 + '@esbuild/android-arm64': 0.28.1 + '@esbuild/android-x64': 0.28.1 + '@esbuild/darwin-arm64': 0.28.1 + '@esbuild/darwin-x64': 0.28.1 + '@esbuild/freebsd-arm64': 0.28.1 + '@esbuild/freebsd-x64': 0.28.1 + '@esbuild/linux-arm': 0.28.1 + '@esbuild/linux-arm64': 0.28.1 + '@esbuild/linux-ia32': 0.28.1 + '@esbuild/linux-loong64': 0.28.1 + '@esbuild/linux-mips64el': 0.28.1 + '@esbuild/linux-ppc64': 0.28.1 + '@esbuild/linux-riscv64': 0.28.1 + '@esbuild/linux-s390x': 0.28.1 + '@esbuild/linux-x64': 0.28.1 + '@esbuild/netbsd-arm64': 0.28.1 + '@esbuild/netbsd-x64': 0.28.1 + '@esbuild/openbsd-arm64': 0.28.1 + '@esbuild/openbsd-x64': 0.28.1 + '@esbuild/openharmony-arm64': 0.28.1 + '@esbuild/sunos-x64': 0.28.1 + '@esbuild/win32-arm64': 0.28.1 + '@esbuild/win32-ia32': 0.28.1 + '@esbuild/win32-x64': 0.28.1 escalade@3.2.0: {} @@ -11406,9 +10847,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.4.0(jiti@2.6.1): + eslint@10.5.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.4.0(jiti@2.6.1)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.6.0 @@ -11439,14 +10880,14 @@ snapshots: natural-compare: 1.4.0 optionator: 0.9.4 optionalDependencies: - jiti: 2.6.1 + jiti: 2.7.0 transitivePeerDependencies: - supports-color espree@11.2.0: dependencies: - acorn: 8.16.0 - acorn-jsx: 5.3.2(acorn@8.16.0) + acorn: 8.17.0 + acorn-jsx: 5.3.2(acorn@8.17.0) eslint-visitor-keys: 5.0.1 esprima@4.0.1: {} @@ -11598,8 +11039,18 @@ snapshots: fast-levenshtein@2.0.6: {} + fast-string-truncated-width@3.0.3: {} + + fast-string-width@3.0.2: + dependencies: + fast-string-truncated-width: 3.0.3 + fast-uri@3.1.2: {} + fast-wrap-ansi@0.2.2: + dependencies: + fast-string-width: 3.0.2 + faye-websocket@0.11.4: dependencies: websocket-driver: 0.7.4 @@ -11608,10 +11059,6 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.5.0(picomatch@4.0.3): - optionalDependencies: - picomatch: 4.0.3 - fdir@6.5.0(picomatch@4.0.4): optionalDependencies: picomatch: 4.0.4 @@ -11730,6 +11177,10 @@ snapshots: get-stream@6.0.1: {} + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + github-from-package@0.0.0: optional: true @@ -11790,10 +11241,6 @@ snapshots: has-symbols@1.1.0: {} - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -11891,6 +11338,13 @@ snapshots: transitivePeerDependencies: - supports-color + https-proxy-agent@9.0.0: + dependencies: + agent-base: 9.0.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + human-signals@2.1.0: {} hyperdyperid@1.2.0: {} @@ -11907,9 +11361,9 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-utils@5.1.0(postcss@8.5.6): + icss-utils@5.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.6 + postcss: 8.5.15 ieee754@1.2.1: optional: true @@ -11965,7 +11419,7 @@ snapshots: is-core-module@2.16.1: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-docker@3.0.0: {} @@ -12011,7 +11465,7 @@ snapshots: is-unicode-supported@2.1.0: {} - is-what@3.14.1: {} + is-what@4.1.16: {} is-wsl@3.1.0: dependencies: @@ -12029,11 +11483,11 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.29.0 + '@babel/core': 7.29.7 '@babel/parser': 7.29.7 '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.7.4 + semver: 7.8.5 transitivePeerDependencies: - supports-color @@ -12074,7 +11528,7 @@ snapshots: '@jest/expect': 30.4.1 '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 co: 4.6.0 dedent: 1.7.2 @@ -12094,15 +11548,15 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)): + jest-cli@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)): dependencies: - '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 chalk: 4.1.2 exit-x: 0.2.2 import-local: 3.2.0 - jest-config: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + jest-config: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) jest-util: 30.4.1 jest-validate: 30.4.1 yargs: 17.7.2 @@ -12113,7 +11567,7 @@ snapshots: - supports-color - ts-node - jest-config@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)): + jest-config@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)): dependencies: '@babel/core': 7.29.7 '@jest/get-type': 30.1.0 @@ -12139,8 +11593,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 25.9.1 - ts-node: 10.9.2(@types/node@25.9.1)(typescript@5.9.3) + '@types/node': 26.0.0 + ts-node: 10.9.2(@types/node@26.0.0)(typescript@6.0.3) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -12195,7 +11649,7 @@ snapshots: '@jest/environment': 30.4.1 '@jest/fake-timers': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-mock: 30.4.1 jest-util: 30.4.1 jest-validate: 30.4.1 @@ -12205,7 +11659,7 @@ snapshots: jest-haste-map@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -12221,7 +11675,7 @@ snapshots: dependencies: mkdirp: 1.0.4 strip-ansi: 6.0.1 - uuid: 14.0.0 + uuid: 14.0.1 xml: 1.0.1 jest-leak-detector@30.4.1: @@ -12271,36 +11725,35 @@ snapshots: jest-mock@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-util: 30.0.5 jest-mock@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 jest-util: 30.4.1 jest-pnp-resolver@1.2.3(jest-resolve@30.4.1): optionalDependencies: jest-resolve: 30.4.1 - jest-preset-angular@16.1.5(26662f94407112e0967a16d5ea795956): + jest-preset-angular@17.0.0(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(canvas@3.0.0)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)))(jsdom@26.1.0(canvas@3.0.0))(typescript@6.0.3): dependencies: - '@angular/compiler-cli': 21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/platform-browser': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)) - '@angular/platform-browser-dynamic': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/compiler@21.2.17)(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))) + '@angular/compiler-cli': 22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/platform-browser': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)) '@jest/environment-jsdom-abstract': 30.4.1(canvas@3.0.0)(jsdom@26.1.0(canvas@3.0.0)) bs-logger: 0.2.6 - esbuild-wasm: 0.28.0 - jest: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + esbuild-wasm: 0.28.1 + jest: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) jest-util: 30.4.1 jsdom: 26.1.0(canvas@3.0.0) pretty-format: 30.4.1 - ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(esbuild@0.28.0)(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(typescript@5.9.3) - typescript: 5.9.3 + ts-jest: 29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)))(typescript@6.0.3) + typescript: 6.0.3 optionalDependencies: - esbuild: 0.28.0 + esbuild: 0.28.1 transitivePeerDependencies: - '@babel/core' - '@jest/transform' @@ -12337,7 +11790,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 emittery: 0.13.1 exit-x: 0.2.2 @@ -12366,7 +11819,7 @@ snapshots: '@jest/test-result': 30.4.1 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 cjs-module-lexer: 2.2.0 collect-v8-coverage: 1.0.3 @@ -12405,7 +11858,7 @@ snapshots: jest-message-util: 30.4.1 jest-util: 30.4.1 pretty-format: 30.4.1 - semver: 7.8.1 + semver: 7.8.5 synckit: 0.11.13 transitivePeerDependencies: - supports-color @@ -12413,7 +11866,7 @@ snapshots: jest-util@30.0.5: dependencies: '@jest/types': 30.0.5 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 @@ -12422,7 +11875,7 @@ snapshots: jest-util@30.4.1: dependencies: '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 chalk: 4.1.2 ci-info: 4.4.0 graceful-fs: 4.2.11 @@ -12441,7 +11894,7 @@ snapshots: dependencies: '@jest/test-result': 30.4.1 '@jest/types': 30.4.1 - '@types/node': 25.9.1 + '@types/node': 26.0.0 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -12455,24 +11908,24 @@ snapshots: jest-worker@27.5.1: dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 merge-stream: 2.0.0 supports-color: 8.1.1 jest-worker@30.4.1: dependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 '@ungap/structured-clone': 1.3.1 jest-util: 30.4.1 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)): + jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)): dependencies: - '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + '@jest/core': 30.4.2(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) '@jest/types': 30.4.1 import-local: 3.2.0 - jest-cli: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + jest-cli: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -12480,7 +11933,7 @@ snapshots: - supports-color - ts-node - jiti@2.6.1: {} + jiti@2.7.0: {} jose@6.2.3: {} @@ -12563,17 +12016,16 @@ snapshots: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.3.0(less@4.4.2)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + less-loader@12.3.2(less@4.6.4)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: - less: 4.4.2 + less: 4.6.4 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) - less@4.4.2: + less@4.6.4: dependencies: - copy-anything: 2.0.6 + copy-anything: 3.0.5 parse-node-version: 1.0.1 - tslib: 2.8.1 optionalDependencies: errno: 0.1.8 graceful-fs: 4.2.11 @@ -12590,41 +12042,23 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-webpack-plugin@4.0.2(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + license-webpack-plugin@4.0.2(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: webpack-sources: 3.5.0 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) lines-and-columns@1.2.4: {} - listr2@9.0.5: + listr2@10.2.1: dependencies: cli-truncate: 5.2.0 - colorette: 2.0.20 eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 - wrap-ansi: 9.0.2 + wrap-ansi: 10.0.0 - lmdb@3.4.4: - dependencies: - msgpackr: 1.11.12 - node-addon-api: 6.1.0 - node-gyp-build-optional-packages: 5.2.2 - ordered-binary: 1.6.1 - weak-lru-cache: 1.2.2 - optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 3.4.4 - '@lmdb/lmdb-darwin-x64': 3.4.4 - '@lmdb/lmdb-linux-arm': 3.4.4 - '@lmdb/lmdb-linux-arm64': 3.4.4 - '@lmdb/lmdb-linux-x64': 3.4.4 - '@lmdb/lmdb-win32-arm64': 3.4.4 - '@lmdb/lmdb-win32-x64': 3.4.4 - optional: true - - lmdb@3.5.1: + lmdb@3.5.4: dependencies: '@harperfast/extended-iterable': 1.0.3 msgpackr: 1.11.12 @@ -12633,13 +12067,13 @@ snapshots: ordered-binary: 1.6.1 weak-lru-cache: 1.2.2 optionalDependencies: - '@lmdb/lmdb-darwin-arm64': 3.5.1 - '@lmdb/lmdb-darwin-x64': 3.5.1 - '@lmdb/lmdb-linux-arm': 3.5.1 - '@lmdb/lmdb-linux-arm64': 3.5.1 - '@lmdb/lmdb-linux-x64': 3.5.1 - '@lmdb/lmdb-win32-arm64': 3.5.1 - '@lmdb/lmdb-win32-x64': 3.5.1 + '@lmdb/lmdb-darwin-arm64': 3.5.4 + '@lmdb/lmdb-darwin-x64': 3.5.4 + '@lmdb/lmdb-linux-arm': 3.5.4 + '@lmdb/lmdb-linux-arm64': 3.5.4 + '@lmdb/lmdb-linux-x64': 3.5.4 + '@lmdb/lmdb-win32-arm64': 3.5.4 + '@lmdb/lmdb-win32-x64': 3.5.4 optional: true loader-runner@4.3.2: {} @@ -12701,7 +12135,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.1 + semver: 7.8.5 make-error@1.3.6: {} @@ -12790,17 +12224,17 @@ snapshots: mimic-response@3.1.0: optional: true - mini-css-extract-plugin@2.9.4(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + mini-css-extract-plugin@2.10.2(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: schema-utils: 4.3.3 tapable: 2.3.3 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) minimalistic-assert@1.0.1: {} minimatch@10.2.5: dependencies: - brace-expansion: 5.0.6 + brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: @@ -12881,9 +12315,7 @@ snapshots: dns-packet: 5.6.1 thunky: 1.1.0 - mute-stream@2.0.0: {} - - nanoid@3.3.11: {} + mute-stream@3.0.0: {} nanoid@3.3.12: {} @@ -12908,54 +12340,54 @@ snapshots: neo-async@2.6.2: {} - ngx-bootstrap-icons@1.9.3(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-bootstrap-icons@1.9.3(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) tslib: 2.8.1 - ngx-color@10.1.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-color@10.1.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) '@ctrl/tinycolor': 4.2.0 material-colors: 1.2.6 tslib: 2.8.1 - ngx-cookie-service@21.3.1(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-cookie-service@22.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) tslib: 2.8.1 - ngx-device-detector@11.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)): + ngx-device-detector@12.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) tslib: 2.8.1 - ngx-ui-tour-core@16.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(rxjs@7.8.2): + ngx-ui-tour-core@17.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(rxjs@7.8.2): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@angular/router': 21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@angular/router': 22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2) rxjs: 7.8.2 tslib: 2.8.1 - ngx-ui-tour-ng-bootstrap@18.0.0(4ccfccfbcf381a309618492b31e99276): + ngx-ui-tour-ng-bootstrap@19.0.0(5d0f9d2d91ce22b3b7a50a70fd62ec66): dependencies: - '@angular/common': 21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) - '@angular/core': 21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2) - '@ng-bootstrap/ng-bootstrap': 20.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@21.2.14(@angular/compiler-cli@21.2.14(@angular/compiler@21.2.17)(typescript@5.9.3))(@angular/compiler@21.2.17))(@popperjs/core@2.11.8)(rxjs@7.8.2) - ngx-ui-tour-core: 16.0.0(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@21.2.14(@angular/common@21.2.17(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@21.2.17(@angular/compiler@21.2.17)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(rxjs@7.8.2) + '@angular/common': 22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2) + '@angular/core': 22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2) + '@ng-bootstrap/ng-bootstrap': 21.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/forms@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(@angular/localize@22.0.5(@angular/compiler-cli@22.0.5(@angular/compiler@22.0.5)(typescript@6.0.3))(@angular/compiler@22.0.5))(@popperjs/core@2.11.8)(rxjs@7.8.2) + ngx-ui-tour-core: 17.0.0(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/router@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(@angular/platform-browser@22.0.5(@angular/common@22.0.5(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2))(rxjs@7.8.2))(@angular/core@22.0.5(@angular/compiler@22.0.5)(rxjs@7.8.2)(zone.js@0.16.2)))(rxjs@7.8.2))(rxjs@7.8.2) tslib: 2.8.1 transitivePeerDependencies: - '@angular/router' - rxjs - node-abi@3.92.0: + node-abi@3.93.0: dependencies: - semver: 7.8.4 + semver: 7.8.5 optional: true node-addon-api@6.1.0: @@ -12964,8 +12396,6 @@ snapshots: node-addon-api@7.1.1: optional: true - node-forge@1.3.3: {} - node-gyp-build-optional-packages@5.2.2: dependencies: detect-libc: 2.1.2 @@ -12978,7 +12408,7 @@ snapshots: graceful-fs: 4.2.11 nopt: 9.0.0 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 tar: 7.5.16 tinyglobby: 0.2.17 undici: 6.26.0 @@ -13004,7 +12434,7 @@ snapshots: npm-install-checks@8.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.5 npm-normalize-package-bin@5.0.0: {} @@ -13012,7 +12442,7 @@ snapshots: dependencies: hosted-git-info: 9.0.3 proc-log: 6.1.0 - semver: 7.7.4 + semver: 7.8.5 validate-npm-package-name: 7.0.2 npm-packlist@10.0.4: @@ -13025,7 +12455,7 @@ snapshots: npm-install-checks: 8.0.0 npm-normalize-package-bin: 5.0.0 npm-package-arg: 13.0.2 - semver: 7.7.4 + semver: 7.8.5 npm-registry-fetch@19.1.1: dependencies: @@ -13099,19 +12529,7 @@ snapshots: type-check: 0.4.0 word-wrap: 1.2.5 - ora@9.0.0: - dependencies: - chalk: 5.6.2 - cli-cursor: 5.0.0 - cli-spinners: 3.4.0 - is-interactive: 2.0.0 - is-unicode-supported: 2.1.0 - log-symbols: 7.0.1 - stdin-discarder: 0.2.2 - string-width: 8.2.1 - strip-ansi: 7.2.0 - - ora@9.3.0: + ora@9.4.0: dependencies: chalk: 5.6.2 cli-cursor: 5.0.0 @@ -13153,8 +12571,9 @@ snapshots: package-json-from-dist@1.0.1: {} - pacote@21.3.1: + pacote@21.5.1: dependencies: + '@gar/promise-retry': 1.0.3 '@npmcli/git': 7.0.2 '@npmcli/installed-package-contents': 4.0.0 '@npmcli/package-json': 7.0.5 @@ -13168,7 +12587,6 @@ snapshots: npm-pick-manifest: 11.0.3 npm-registry-fetch: 19.1.1 proc-log: 6.1.0 - promise-retry: 2.0.1 sigstore: 4.1.1 ssri: 13.0.1 tar: 7.5.16 @@ -13190,9 +12608,9 @@ snapshots: parse-node-version@1.0.1: {} - parse5-html-rewriting-stream@8.0.0: + parse5-html-rewriting-stream@8.0.1: dependencies: - entities: 6.0.1 + entities: 8.0.0 parse5: 8.0.1 parse5-sax-parser: 8.0.0 @@ -13232,16 +12650,14 @@ snapshots: path-to-regexp@8.4.2: {} - pdfjs-dist@5.7.284: + pdfjs-dist@6.0.227: optionalDependencies: - '@napi-rs/canvas': 0.1.100 + '@napi-rs/canvas': 1.0.2 picocolors@1.1.1: {} picomatch@2.3.2: {} - picomatch@4.0.3: {} - picomatch@4.0.4: {} pify@4.0.1: @@ -13249,7 +12665,7 @@ snapshots: pirates@4.0.7: {} - piscina@5.1.4: + piscina@5.2.0: optionalDependencies: '@napi-rs/nice': 1.1.1 @@ -13259,47 +12675,56 @@ snapshots: dependencies: find-up: 4.1.0 - playwright-core@1.60.0: {} - - playwright@1.60.0: + pkijs@3.4.0: dependencies: - playwright-core: 1.60.0 + '@noble/hashes': 1.4.0 + asn1js: 3.0.10 + bytestreamjs: 2.0.1 + pvtsutils: 1.3.6 + pvutils: 1.1.5 + tslib: 2.8.1 + + playwright-core@1.61.1: {} + + playwright@1.61.1: + dependencies: + playwright-core: 1.61.1 optionalDependencies: fsevents: 2.3.2 - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + postcss-loader@8.2.1(postcss@8.5.13)(typescript@6.0.3)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: - cosmiconfig: 9.0.0(typescript@5.9.3) - jiti: 2.6.1 - postcss: 8.5.6 - semver: 7.7.3 + cosmiconfig: 9.0.0(typescript@6.0.3) + jiti: 2.7.0 + postcss: 8.5.13 + semver: 7.8.5 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) transitivePeerDependencies: - typescript postcss-media-query-parser@0.2.3: {} - postcss-modules-extract-imports@3.1.0(postcss@8.5.6): + postcss-modules-extract-imports@3.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.6 + postcss: 8.5.15 - postcss-modules-local-by-default@4.2.0(postcss@8.5.6): + postcss-modules-local-by-default@4.2.0(postcss@8.5.15): dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 postcss-selector-parser: 7.1.1 postcss-value-parser: 4.2.0 - postcss-modules-scope@3.2.1(postcss@8.5.6): + postcss-modules-scope@3.2.1(postcss@8.5.15): dependencies: - postcss: 8.5.6 + postcss: 8.5.15 postcss-selector-parser: 7.1.1 - postcss-modules-values@4.0.0(postcss@8.5.6): + postcss-modules-values@4.0.0(postcss@8.5.15): dependencies: - icss-utils: 5.1.0(postcss@8.5.6) - postcss: 8.5.6 + icss-utils: 5.1.0(postcss@8.5.15) + postcss: 8.5.15 postcss-safe-parser@7.0.1(postcss@8.5.15): dependencies: @@ -13312,15 +12737,15 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.5.15: + postcss@8.5.13: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 - postcss@8.5.6: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -13334,20 +12759,20 @@ snapshots: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.92.0 + node-abi: 3.93.0 pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 - tar-fs: 2.1.4 + tar-fs: 2.1.5 tunnel-agent: 0.6.0 optional: true prelude-ls@1.2.1: {} - prettier-plugin-organize-imports@4.3.0(prettier@3.4.2)(typescript@5.9.3): + prettier-plugin-organize-imports@4.3.0(prettier@3.4.2)(typescript@6.0.3): dependencies: prettier: 3.4.2 - typescript: 5.9.3 + typescript: 6.0.3 prettier@3.4.2: {} @@ -13374,11 +12799,6 @@ snapshots: process-nextick-args@2.0.1: {} - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - proxy-addr@2.0.7: dependencies: forwarded: 0.2.0 @@ -13397,14 +12817,16 @@ snapshots: pure-rand@7.0.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.5: {} + qs@6.15.2: dependencies: side-channel: 1.1.0 - randombytes@2.1.0: - dependencies: - safe-buffer: 5.2.1 - range-parser@1.2.1: {} raw-body@2.5.3: @@ -13496,12 +12918,14 @@ snapshots: resolve-from@5.0.0: {} + resolve-pkg-maps@1.0.0: {} + resolve-url-loader@5.0.0: dependencies: adjust-sourcemap-loader: 4.0.0 convert-source-map: 1.9.0 loader-utils: 2.0.4 - postcss: 8.5.6 + postcss: 8.5.15 source-map: 0.6.1 resolve@1.22.11: @@ -13515,55 +12939,40 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry@0.12.0: {} - retry@0.13.1: {} rfdc@1.4.1: {} - rolldown@1.0.0-beta.58(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): + rollup@4.60.2: dependencies: - '@oxc-project/types': 0.106.0 - '@rolldown/pluginutils': 1.0.0-beta.58 + '@types/estree': 1.0.8 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.58 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.58 - '@rolldown/binding-darwin-x64': 1.0.0-beta.58 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.58 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.58 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.58 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.58 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.58 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.58 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.58 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.58(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.58 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.58 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' - - rolldown@1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0): - dependencies: - '@oxc-project/types': 0.113.0 - '@rolldown/pluginutils': 1.0.0-rc.4 - optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-rc.4 - '@rolldown/binding-darwin-arm64': 1.0.0-rc.4 - '@rolldown/binding-darwin-x64': 1.0.0-rc.4 - '@rolldown/binding-freebsd-x64': 1.0.0-rc.4 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-rc.4 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-rc.4 - '@rolldown/binding-linux-arm64-musl': 1.0.0-rc.4 - '@rolldown/binding-linux-x64-gnu': 1.0.0-rc.4 - '@rolldown/binding-linux-x64-musl': 1.0.0-rc.4 - '@rolldown/binding-openharmony-arm64': 1.0.0-rc.4 - '@rolldown/binding-wasm32-wasi': 1.0.0-rc.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) - '@rolldown/binding-win32-arm64-msvc': 1.0.0-rc.4 - '@rolldown/binding-win32-x64-msvc': 1.0.0-rc.4 - transitivePeerDependencies: - - '@emnapi/core' - - '@emnapi/runtime' + '@rollup/rollup-android-arm-eabi': 4.60.2 + '@rollup/rollup-android-arm64': 4.60.2 + '@rollup/rollup-darwin-arm64': 4.60.2 + '@rollup/rollup-darwin-x64': 4.60.2 + '@rollup/rollup-freebsd-arm64': 4.60.2 + '@rollup/rollup-freebsd-x64': 4.60.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.2 + '@rollup/rollup-linux-arm-musleabihf': 4.60.2 + '@rollup/rollup-linux-arm64-gnu': 4.60.2 + '@rollup/rollup-linux-arm64-musl': 4.60.2 + '@rollup/rollup-linux-loong64-gnu': 4.60.2 + '@rollup/rollup-linux-loong64-musl': 4.60.2 + '@rollup/rollup-linux-ppc64-gnu': 4.60.2 + '@rollup/rollup-linux-ppc64-musl': 4.60.2 + '@rollup/rollup-linux-riscv64-gnu': 4.60.2 + '@rollup/rollup-linux-riscv64-musl': 4.60.2 + '@rollup/rollup-linux-s390x-gnu': 4.60.2 + '@rollup/rollup-linux-x64-gnu': 4.60.2 + '@rollup/rollup-linux-x64-musl': 4.60.2 + '@rollup/rollup-openbsd-x64': 4.60.2 + '@rollup/rollup-openharmony-arm64': 4.60.2 + '@rollup/rollup-win32-arm64-msvc': 4.60.2 + '@rollup/rollup-win32-ia32-msvc': 4.60.2 + '@rollup/rollup-win32-x64-gnu': 4.60.2 + '@rollup/rollup-win32-x64-msvc': 4.60.2 + fsevents: 2.3.3 rollup@4.61.0: dependencies: @@ -13620,22 +13029,14 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.6(sass@1.97.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + sass-loader@16.0.7(sass@1.99.0)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: neo-async: 2.6.2 optionalDependencies: - sass: 1.97.1 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + sass: 1.99.0 + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) - sass@1.97.1: - dependencies: - chokidar: 4.0.3 - immutable: 5.1.6 - source-map-js: 1.2.1 - optionalDependencies: - '@parcel/watcher': 2.5.6 - - sass@1.97.3: + sass@1.99.0: dependencies: chokidar: 4.0.3 immutable: 5.1.6 @@ -13659,24 +13060,21 @@ snapshots: select-hose@2.0.0: {} - selfsigned@2.4.1: + selfsigned@5.5.0: dependencies: - '@types/node-forge': 1.3.14 - node-forge: 1.3.3 + '@peculiar/x509': 1.14.3 + pkijs: 3.4.0 semver@5.7.2: optional: true semver@6.3.1: {} - semver@7.7.3: {} - semver@7.7.4: {} - semver@7.8.1: {} + semver@7.8.0: {} - semver@7.8.4: - optional: true + semver@7.8.5: {} send@0.19.2: dependencies: @@ -13712,9 +13110,7 @@ snapshots: transitivePeerDependencies: - supports-color - serialize-javascript@6.0.2: - dependencies: - randombytes: 2.1.0 + serialize-javascript@7.0.7: {} serve-index@1.9.2: dependencies: @@ -13855,11 +13251,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + source-map-loader@5.0.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) source-map-support@0.5.13: dependencies: @@ -13919,8 +13315,6 @@ snapshots: statuses@2.0.2: {} - stdin-discarder@0.2.2: {} - stdin-discarder@0.3.2: {} string-length@4.0.2: @@ -13967,8 +13361,6 @@ snapshots: dependencies: ansi-regex: 6.2.2 - strip-bom@3.0.0: {} - strip-bom@4.0.0: {} strip-final-newline@2.0.0: {} @@ -13996,7 +13388,7 @@ snapshots: tapable@2.3.3: {} - tar-fs@2.1.4: + tar-fs@2.1.5: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 @@ -14021,16 +13413,16 @@ snapshots: minizlib: 3.1.0 yallist: 5.0.0 - terser-webpack-plugin@5.6.1(esbuild@0.27.2)(postcss@8.5.6)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + terser-webpack-plugin@5.6.1(esbuild@0.28.1)(postcss@8.5.13)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 terser: 5.48.0 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) optionalDependencies: - esbuild: 0.27.2 - postcss: 8.5.6 + esbuild: 0.28.1 + postcss: 8.5.13 terser-webpack-plugin@5.6.1(postcss@8.5.15)(webpack@5.107.2(postcss@8.5.15)): dependencies: @@ -14042,17 +13434,17 @@ snapshots: optionalDependencies: postcss: 8.5.15 - terser@5.44.1: + terser@5.46.2: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 + acorn: 8.17.0 commander: 2.20.3 source-map-support: 0.5.21 terser@5.48.0: dependencies: '@jridgewell/source-map': 0.3.11 - acorn: 8.16.0 + acorn: 8.17.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -14068,7 +13460,7 @@ snapshots: thunky@1.1.0: {} - tinyglobby@0.2.15: + tinyglobby@0.2.16: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -14104,59 +13496,57 @@ snapshots: dependencies: tslib: 2.8.1 - tree-kill@1.2.2: {} - - ts-api-utils@2.5.0(typescript@5.9.3): + ts-api-utils@2.5.0(typescript@6.0.3): dependencies: - typescript: 5.9.3 + typescript: 6.0.3 - ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(esbuild@0.28.0)(jest-util@30.4.1)(jest@30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)))(typescript@5.9.3): + ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@30.4.1)(@jest/types@30.4.1)(babel-jest@30.4.1(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@30.4.1)(jest@30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)))(typescript@6.0.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.9 - jest: 30.4.2(@types/node@25.9.1)(ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3)) + jest: 30.4.2(@types/node@26.0.0)(ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.8.1 + semver: 7.8.5 type-fest: 4.41.0 - typescript: 5.9.3 + typescript: 6.0.3 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.29.7 '@jest/transform': 30.4.1 '@jest/types': 30.4.1 babel-jest: 30.4.1(@babel/core@7.29.7) - esbuild: 0.28.0 + esbuild: 0.28.1 jest-util: 30.4.1 - ts-node@10.9.2(@types/node@25.9.1)(typescript@5.9.3): + ts-node@10.9.2(@types/node@26.0.0)(typescript@6.0.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 25.9.1 + '@types/node': 26.0.0 acorn: 8.16.0 acorn-walk: 8.3.1 arg: 4.1.3 create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.9.3 + typescript: 6.0.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 - tsconfig-paths@4.2.0: - dependencies: - json5: 2.2.3 - minimist: 1.2.8 - strip-bom: 3.0.0 + tslib@1.14.1: {} tslib@2.8.1: {} + tsyringe@4.10.0: + dependencies: + tslib: 1.14.1 + tuf-js@4.1.0: dependencies: '@tufjs/models': 4.1.0 @@ -14195,19 +13585,15 @@ snapshots: typed-assert@1.0.9: {} - typescript@5.9.3: {} + typescript@6.0.3: {} uglify-js@3.19.3: optional: true - undici-types@7.24.6: {} + undici-types@8.3.0: {} undici@6.26.0: {} - undici@7.18.2: {} - - undici@7.24.4: {} - unicode-canonical-property-names-ecmascript@2.0.1: {} unicode-match-property-ecmascript@2.0.0: @@ -14273,7 +13659,7 @@ snapshots: utils-merge@1.0.1: {} - uuid@14.0.0: {} + uuid@14.0.1: {} uuid@8.3.2: {} @@ -14289,38 +13675,38 @@ snapshots: vary@1.1.2: {} - vite@7.3.0(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.1)(terser@5.44.1)(yaml@2.7.0): - dependencies: - esbuild: 0.27.2 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.61.0 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.9.1 - fsevents: 2.3.3 - jiti: 2.6.1 - less: 4.4.2 - sass: 1.97.1 - terser: 5.44.1 - yaml: 2.7.0 - - vite@7.3.2(@types/node@25.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.97.3)(terser@5.44.1)(yaml@2.7.0): + vite@7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.46.2)(yaml@2.7.0): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 postcss: 8.5.15 rollup: 4.61.0 - tinyglobby: 0.2.15 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.1 + '@types/node': 26.0.0 fsevents: 2.3.3 - jiti: 2.6.1 - less: 4.4.2 - sass: 1.97.3 - terser: 5.44.1 + jiti: 2.7.0 + less: 4.6.4 + sass: 1.99.0 + terser: 5.46.2 + yaml: 2.7.0 + + vite@7.3.5(@types/node@26.0.0)(jiti@2.7.0)(less@4.6.4)(sass@1.99.0)(terser@5.48.0)(yaml@2.7.0): + dependencies: + esbuild: 0.27.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.61.0 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 26.0.0 + fsevents: 2.3.3 + jiti: 2.7.0 + less: 4.6.4 + sass: 1.99.0 + terser: 5.48.0 yaml: 2.7.0 w3c-xmlserializer@5.0.0: @@ -14331,11 +13717,6 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@2.5.0: - dependencies: - glob-to-regexp: 0.4.1 - graceful-fs: 4.2.11 - watchpack@2.5.1: dependencies: glob-to-regexp: 0.4.1 @@ -14350,7 +13731,7 @@ snapshots: webidl-conversions@7.0.0: {} - webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + webpack-dev-middleware@7.4.5(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: colorette: 2.0.20 memfs: 4.56.10(tslib@2.8.1) @@ -14359,7 +13740,7 @@ snapshots: range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) transitivePeerDependencies: - tslib @@ -14376,7 +13757,19 @@ snapshots: transitivePeerDependencies: - tslib - webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + webpack-dev-middleware@8.0.3(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): + dependencies: + memfs: 4.56.10(tslib@2.8.1) + mime-types: 3.0.2 + on-finished: 2.4.1 + range-parser: 1.2.1 + schema-utils: 4.3.3 + optionalDependencies: + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) + transitivePeerDependencies: + - tslib + + webpack-dev-server@5.2.3(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -14400,14 +13793,14 @@ snapshots: open: 10.2.0 p-retry: 6.2.1 schema-utils: 4.3.3 - selfsigned: 2.4.1 + selfsigned: 5.5.0 serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + webpack-dev-middleware: 7.4.5(tslib@2.8.1)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) ws: 8.21.0 optionalDependencies: - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) transitivePeerDependencies: - bufferutil - debug @@ -14415,7 +13808,7 @@ snapshots: - tslib - utf-8-validate - webpack-dev-server@5.2.2(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)): + webpack-dev-server@5.2.3(tslib@2.8.1)(webpack@5.107.2(postcss@8.5.15)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 @@ -14439,7 +13832,7 @@ snapshots: open: 10.2.0 p-retry: 6.2.1 schema-utils: 4.3.3 - selfsigned: 2.4.1 + selfsigned: 5.5.0 serve-index: 1.9.2 sockjs: 0.3.24 spdy: 4.0.2 @@ -14462,14 +13855,14 @@ snapshots: webpack-sources@3.5.0: {} - webpack-subresource-integrity@5.1.0(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)): + webpack-subresource-integrity@5.1.0(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)): dependencies: typed-assert: 1.0.9 - webpack: 5.104.1(esbuild@0.27.2)(postcss@8.5.6) + webpack: 5.106.2(esbuild@0.28.1)(postcss@8.5.13) webpack-virtual-modules@0.6.2: {} - webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6): + webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.9 @@ -14477,8 +13870,8 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 - acorn: 8.16.0 - acorn-import-phases: 1.0.4(acorn@8.16.0) + acorn: 8.17.0 + acorn-import-phases: 1.0.4(acorn@8.17.0) browserslist: 4.28.2 chrome-trace-event: 1.0.4 enhanced-resolve: 5.22.1 @@ -14487,13 +13880,12 @@ snapshots: events: 3.3.0 glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - json-parse-even-better-errors: 2.3.1 loader-runner: 4.3.2 - mime-types: 2.1.35 + mime-db: 1.54.0 neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.3 - terser-webpack-plugin: 5.6.1(esbuild@0.27.2)(postcss@8.5.6)(webpack@5.104.1(esbuild@0.27.2)(postcss@8.5.6)) + terser-webpack-plugin: 5.6.1(esbuild@0.28.1)(postcss@8.5.13)(webpack@5.106.2(esbuild@0.28.1)(postcss@8.5.13)) watchpack: 2.5.1 webpack-sources: 3.5.0 transitivePeerDependencies: @@ -14582,11 +13974,11 @@ snapshots: wordwrap@1.0.0: {} - wrap-ansi@6.2.0: + wrap-ansi@10.0.0: dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 wrap-ansi@7.0.0: dependencies: @@ -14668,16 +14060,17 @@ snapshots: yocto-queue@0.1.0: {} - yoctocolors-cjs@2.1.3: {} - yoctocolors@2.1.2: {} - zod-to-json-schema@3.25.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@4.4.2): dependencies: - zod: 4.3.6 + zod: 4.4.2 zod@3.25.76: {} zod@4.3.6: {} - zone.js@0.16.2: {} + zod@4.4.2: {} + + zone.js@0.16.2: + optional: true diff --git a/src-ui/pnpm-workspace.yaml b/src-ui/pnpm-workspace.yaml new file mode 100644 index 000000000..0065ff09b --- /dev/null +++ b/src-ui/pnpm-workspace.yaml @@ -0,0 +1,21 @@ +packages: + - "." +minimumReleaseAge: 10080 +trustPolicy: no-downgrade +allowBuilds: + "@parcel/watcher": true + canvas: true + esbuild: true + lmdb: true + msgpackr-extract: true + normalize-diacritics: false + unrs-resolver: false +onlyBuiltDependencies: + - "@parcel/watcher" + - canvas + - esbuild + - lmdb + - msgpackr-extract +ignoredBuiltDependencies: + - normalize-diacritics + - unrs-resolver diff --git a/src-ui/setup-jest.ts b/src-ui/setup-jest.ts index 86e447b59..b90d97b2f 100644 --- a/src-ui/setup-jest.ts +++ b/src-ui/setup-jest.ts @@ -1,9 +1,9 @@ import '@angular/localize/init' import { jest } from '@jest/globals' -import { setupZoneTestEnv } from 'jest-preset-angular/setup-env/zone' +import { setupZonelessTestEnv } from 'jest-preset-angular/setup-env/zoneless' import { TextDecoder, TextEncoder } from 'node:util' if (process.env.NODE_ENV === 'test') { - setupZoneTestEnv() + setupZonelessTestEnv() } ;(globalThis as any).TextEncoder = TextEncoder as unknown as { new (): TextEncoder diff --git a/src-ui/src/app/app.component.spec.ts b/src-ui/src/app/app.component.spec.ts index 0d23d4feb..721aa37b3 100644 --- a/src-ui/src/app/app.component.spec.ts +++ b/src-ui/src/app/app.component.spec.ts @@ -1,11 +1,6 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - fakeAsync, - TestBed, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { Router, RouterModule } from '@angular/router' import { NgbModalModule } from '@ng-bootstrap/ng-bootstrap' import { allIcons, NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @@ -72,7 +67,8 @@ describe('AppComponent', () => { component = fixture.componentInstance }) - it('should initialize the tour service & toggle class on body for styling', fakeAsync(() => { + it('should initialize the tour service & toggle class on body for styling', () => { + jest.useFakeTimers() jest.spyOn(console, 'warn').mockImplementation(() => {}) fixture.detectChanges() const tourSpy = jest.spyOn(tourService, 'initialize') @@ -81,9 +77,10 @@ describe('AppComponent', () => { tourService.start() expect(document.body.classList).toContain('tour-active') tourService.end() - tick(500) + jest.advanceTimersByTime(500) expect(document.body.classList).not.toContain('tour-active') - })) + jest.useRealTimers() + }) it('should display toast on document consumed with link if user has access', () => { const navigateSpy = jest.spyOn(router, 'navigate') diff --git a/src-ui/src/app/components/admin/config/config.component.html b/src-ui/src/app/components/admin/config/config.component.html index 38ddbf3a7..7c8a65628 100644 --- a/src-ui/src/app/components/admin/config/config.component.html +++ b/src-ui/src/app/components/admin/config/config.component.html @@ -59,10 +59,10 @@
- +
- +
diff --git a/src-ui/src/app/components/admin/config/config.component.ts b/src-ui/src/app/components/admin/config/config.component.ts index 9e69c9dc6..9c2946438 100644 --- a/src-ui/src/app/components/admin/config/config.component.ts +++ b/src-ui/src/app/components/admin/config/config.component.ts @@ -97,11 +97,11 @@ export class ConfigComponent .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError($localize`Error retrieving config`, e) }, }) @@ -162,20 +162,20 @@ export class ConfigComponent } public saveConfig() { - this.loading = true + this.loading.set(true) this.configService .saveConfig(this.configForm.value as PaperlessConfig) .pipe(takeUntil(this.unsubscribeNotifier), first()) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) this.store.next(config) this.settingsService.initializeSettings().subscribe() this.toastService.showInfo($localize`Configuration updated`) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError( $localize`An error occurred updating configuration`, e @@ -189,20 +189,20 @@ export class ConfigComponent } public uploadFile(file: File, key: string) { - this.loading = true + this.loading.set(true) this.configService .uploadFile(file, this.configForm.value['id'], key) .pipe(takeUntil(this.unsubscribeNotifier), first()) .subscribe({ next: (config) => { - this.loading = false + this.loading.set(false) this.initialize(config) this.store.next(config) this.settingsService.initializeSettings().subscribe() this.toastService.showInfo($localize`File successfully updated`) }, error: (e) => { - this.loading = false + this.loading.set(false) this.toastService.showError( $localize`An error occurred uploading file`, e diff --git a/src-ui/src/app/components/admin/logs/logs.component.html b/src-ui/src/app/components/admin/logs/logs.component.html index 17ceb1e89..2099429b3 100644 --- a/src-ui/src/app/components/admin/logs/logs.component.html +++ b/src-ui/src/app/components/admin/logs/logs.component.html @@ -11,30 +11,30 @@ type="number" min="100" step="100" - [(ngModel)]="limit" - (ngModelChange)="onLimitChange($event)" + [ngModel]="limit()" + (ngModelChange)="limit.set($event); onLimitChange($event)" style="width: 100px;"> lines
- +
-
    - @for (logFile of logFiles; track logFile) { +
      + @for (logFile of logFiles(); track logFile) {
    • {{logFile}}.log
    • } - @if (loading || !logFiles.length) { + @if (loading() || !logFiles().length) {
      - @if (!logFiles.length) { + @if (!logFiles().length) { Loading... }
      @@ -42,13 +42,13 @@
    - @if (loading && !logFiles.length) { + @if (loading() && !logFiles().length) {
    Loading...
    } @else { - @for (log of logs; track log) { + @for (log of logs(); track log) {

    {{log.message}}

    } } @@ -56,7 +56,7 @@ @if (canViewSystemStatus) {
    - + Objects without an owner can be viewed and edited by all users
    diff --git a/src-ui/src/app/components/admin/settings/settings.component.spec.ts b/src-ui/src/app/components/admin/settings/settings.component.spec.ts index 854444e98..540e7b5c7 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.spec.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.spec.ts @@ -99,6 +99,13 @@ const status: SystemStatus = { llmindex_status: SystemStatusItemStatus.DISABLED, llmindex_last_modified: new Date().toISOString(), llmindex_error: null, + summary: { + days: 30, + total_count: 12, + pending_count: 1, + success_count: 10, + failure_count: 1, + }, }, } @@ -161,7 +168,7 @@ describe('SettingsComponent', () => { viewportScroller = TestBed.inject(ViewportScroller) toastService = TestBed.inject(ToastService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = users[0] + settingsService.currentUser.set(users[0]) userService = TestBed.inject(UserService) permissionsService = TestBed.inject(PermissionsService) modalService = TestBed.inject(NgbModal) @@ -202,7 +209,7 @@ describe('SettingsComponent', () => { fixture.detectChanges() } - it('should support tabbed settings & change URL, prevent navigation if dirty confirmation rejected', () => { + it('should support tabbed settings & change URL, prevent navigation if dirty confirmation rejected', async () => { completeSetup() const navigateSpy = jest.spyOn(router, 'navigate') const tabButtons = fixture.debugElement.queryAll(By.directive(NgbNavLink)) @@ -210,16 +217,19 @@ describe('SettingsComponent', () => { expect(navigateSpy).toHaveBeenCalledWith(['settings', 'documents']) tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click')) expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions']) + await fixture.whenStable() const initSpy = jest.spyOn(component, 'initialize') component.isDirty = true // mock dirty navigateSpy.mockResolvedValueOnce(false) // nav rejected cause dirty tabButtons[0].nativeElement.dispatchEvent(new MouseEvent('click')) + await fixture.whenStable() expect(navigateSpy).toHaveBeenCalledWith(['settings', 'general']) expect(initSpy).not.toHaveBeenCalled() navigateSpy.mockResolvedValueOnce(true) // nav accepted even though dirty tabButtons[2].nativeElement.dispatchEvent(new MouseEvent('click')) + await fixture.whenStable() expect(navigateSpy).toHaveBeenCalledWith(['settings', 'permissions']) expect(initSpy).toHaveBeenCalled() }) @@ -340,13 +350,13 @@ describe('SettingsComponent', () => { type === PermissionType.SystemMonitoring ) completeSetup() - expect(component['systemStatus']).toEqual(status) // private + expect(component.systemStatus()).toEqual(status) expect(component.systemStatusHasErrors).toBeTruthy() // coverage - component['systemStatus'].database.status = SystemStatusItemStatus.OK - component['systemStatus'].tasks.redis_status = SystemStatusItemStatus.OK - component['systemStatus'].tasks.celery_status = SystemStatusItemStatus.OK - component['systemStatus'].tasks.sanity_check_status = + component.systemStatus().database.status = SystemStatusItemStatus.OK + component.systemStatus().tasks.redis_status = SystemStatusItemStatus.OK + component.systemStatus().tasks.celery_status = SystemStatusItemStatus.OK + component.systemStatus().tasks.sanity_check_status = SystemStatusItemStatus.OK expect(component.systemStatusHasErrors).toBeFalsy() }) diff --git a/src-ui/src/app/components/admin/settings/settings.component.ts b/src-ui/src/app/components/admin/settings/settings.component.ts index 8d6805bbc..86e5d36cd 100644 --- a/src-ui/src/app/components/admin/settings/settings.component.ts +++ b/src-ui/src/app/components/admin/settings/settings.component.ts @@ -6,6 +6,7 @@ import { OnDestroy, OnInit, inject, + signal, } from '@angular/core' import { FormControl, @@ -189,10 +190,10 @@ export class SettingsComponent unsubscribeNotifier: Subject = new Subject() savePending: boolean = false - users: User[] - groups: Group[] + readonly users = signal(undefined) + readonly groups = signal(undefined) - public systemStatus: SystemStatus + public readonly systemStatus = signal(undefined) public readonly GlobalSearchType = GlobalSearchType @@ -203,16 +204,18 @@ export class SettingsComponent public readonly documentDetailFieldOptions = documentDetailFieldOptions get systemStatusHasErrors(): boolean { + const status = this.systemStatus() + if (!status) { + return false + } return ( - this.systemStatus.database.status === SystemStatusItemStatus.ERROR || - this.systemStatus.tasks.redis_status === SystemStatusItemStatus.ERROR || - this.systemStatus.tasks.celery_status === SystemStatusItemStatus.ERROR || - this.systemStatus.tasks.index_status === SystemStatusItemStatus.ERROR || - this.systemStatus.tasks.classifier_status === - SystemStatusItemStatus.ERROR || - this.systemStatus.tasks.sanity_check_status === - SystemStatusItemStatus.ERROR || - this.systemStatus.websocket_connected === SystemStatusItemStatus.ERROR + status.database.status === SystemStatusItemStatus.ERROR || + status.tasks.redis_status === SystemStatusItemStatus.ERROR || + status.tasks.celery_status === SystemStatusItemStatus.ERROR || + status.tasks.index_status === SystemStatusItemStatus.ERROR || + status.tasks.classifier_status === SystemStatusItemStatus.ERROR || + status.tasks.sanity_check_status === SystemStatusItemStatus.ERROR || + status.websocket_connected === SystemStatusItemStatus.ERROR ) } @@ -246,7 +249,7 @@ export class SettingsComponent .pipe(first()) .subscribe({ next: (r) => { - this.users = r.results + this.users.set(r.results) }, error: (e) => { this.toastService.showError($localize`Error retrieving users`, e) @@ -265,7 +268,7 @@ export class SettingsComponent .pipe(first()) .subscribe({ next: (r) => { - this.groups = r.results + this.groups.set(r.results) }, error: (e) => { this.toastService.showError($localize`Error retrieving groups`, e) @@ -431,7 +434,7 @@ export class SettingsComponent if (this.canViewSystemStatus) { this.systemStatusService.get().subscribe((status) => { - this.systemStatus = status + this.systemStatus.set(status) }) } } @@ -664,6 +667,6 @@ export class SettingsComponent size: 'xl', } ) - modal.componentInstance.status = this.systemStatus + modal.componentInstance.status.set(this.systemStatus()) } } diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.html b/src-ui/src/app/components/admin/tasks/tasks.component.html index e1d6bc900..05a224a0a 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.html +++ b/src-ui/src/app/components/admin/tasks/tasks.component.html @@ -11,7 +11,7 @@ -
    @@ -107,7 +107,7 @@ { ) req.flush({ count: 2, results: [tasks[0], tasks[1]] }) - expect(component.totalTasks).toBe(2) + expect(component.totalTasks()).toBe(2) }) it('should apply task type and trigger source filters to the server-side task query', () => { @@ -435,8 +435,8 @@ describe('TasksComponent', () => { .flush(pageTwoTasks) expect(component.page).toBe(2) - expect(component.totalTasks).toBe(30) - expect(component.pagedTasks).toEqual([tasks[0]]) + expect(component.totalTasks()).toBe(30) + expect(component.pagedTasks()).toEqual([tasks[0]]) }) it('should not replace section counts with current-page counts', () => { diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.ts b/src-ui/src/app/components/admin/tasks/tasks.component.ts index 276dc6a8f..edf606c12 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.ts +++ b/src-ui/src/app/components/admin/tasks/tasks.component.ts @@ -1,5 +1,5 @@ import { JsonPipe, NgTemplateOutlet } from '@angular/common' -import { Component, inject, OnDestroy, OnInit } from '@angular/core' +import { Component, inject, OnDestroy, OnInit, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { Router, RouterLink } from '@angular/router' import { @@ -166,14 +166,14 @@ export class TasksComponent public autoRefreshEnabled: boolean = true public readonly pageSize = 25 public page: number = 1 - public totalTasks: number = 0 - public sectionCounts: Record = { + readonly totalTasks = signal(0) + readonly sectionCounts = signal>({ [TaskSection.All]: 0, [TaskSection.NeedsAttention]: 0, [TaskSection.InProgress]: 0, [TaskSection.Completed]: 0, - } - public pagedTasks: PaperlessTask[] = [] + }) + readonly pagedTasks = signal([]) public selectedSection: TaskSection = TaskSection.All public selectedTaskType: PaperlessTaskType | null = null public selectedTriggerSource: PaperlessTaskTriggerSource | null = null @@ -346,7 +346,7 @@ export class TasksComponent backdrop: 'static', }) modal.componentInstance.title = $localize`Confirm Dismiss All` - modal.componentInstance.messageBold = $localize`Dismiss all ${this.totalTasks} tasks?` + modal.componentInstance.messageBold = $localize`Dismiss all ${this.totalTasks()} tasks?` modal.componentInstance.btnClass = 'btn-warning' modal.componentInstance.btnCaption = $localize`Dismiss` modal.componentInstance.confirmClicked.pipe(first()).subscribe(() => { @@ -465,7 +465,7 @@ export class TasksComponent } tasksForSection(section: TaskSection): PaperlessTask[] { - let tasks = this.pagedTasks.filter((task) => + let tasks = this.pagedTasks().filter((task) => this.taskBelongsToSection(task, section) ) @@ -477,7 +477,14 @@ export class TasksComponent } sectionCount(section: TaskSection): number { - return this.sectionCounts[section] + return this.sectionCounts()[section] + } + + private setSectionCount(section: TaskSection, count: number) { + this.sectionCounts.update((counts) => ({ + ...counts, + [section]: count, + })) } sectionShowsResults(section: TaskSection): boolean { @@ -652,7 +659,7 @@ export class TasksComponent ? this.sections : [this.selectedSection] - return this.pagedTasks.filter( + return this.pagedTasks().filter( (task) => sections.some((section) => this.taskBelongsToSection(task, section)) && this.taskMatchesFilters(task, { taskType, triggerSource }) @@ -664,10 +671,12 @@ export class TasksComponent .statusCounts(this.getParamsForSection(TaskSection.All)) .pipe(first(), takeUntil(this.unsubscribeNotifier)) .subscribe((counts) => { - this.sectionCounts[TaskSection.All] = counts.all - this.sectionCounts[TaskSection.NeedsAttention] = counts.needs_attention - this.sectionCounts[TaskSection.InProgress] = counts.in_progress - this.sectionCounts[TaskSection.Completed] = counts.completed + this.sectionCounts.set({ + [TaskSection.All]: counts.all, + [TaskSection.NeedsAttention]: counts.needs_attention, + [TaskSection.InProgress]: counts.in_progress, + [TaskSection.Completed]: counts.completed, + }) }) } @@ -723,7 +732,7 @@ export class TasksComponent this.reloadSectionCounts() - this.loading = true + this.loading.set(true) this.tasksService .list( this.page, @@ -733,24 +742,24 @@ export class TasksComponent .pipe(first(), takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (result) => { - this.pagedTasks = result.results - this.totalTasks = result.count - this.sectionCounts[TaskSection.All] = result.count + this.pagedTasks.set(result.results) + this.totalTasks.set(result.count) + this.setSectionCount(TaskSection.All, result.count) if (this.selectedSection !== TaskSection.All) { - this.sectionCounts[this.selectedSection] = result.count + this.setSectionCount(this.selectedSection, result.count) } - this.loading = false + this.loading.set(false) if ( this.page > 1 && - this.pagedTasks.length === 0 && - this.totalTasks > 0 + this.pagedTasks().length === 0 && + this.totalTasks() > 0 ) { this.page -= 1 this.reloadPage() } }, error: () => { - this.loading = false + this.loading.set(false) }, }) } diff --git a/src-ui/src/app/components/admin/trash/trash.component.html b/src-ui/src/app/components/admin/trash/trash.component.html index c729d26f2..9f7037efd 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.html +++ b/src-ui/src/app/components/admin/trash/trash.component.html @@ -4,22 +4,22 @@ info="Manage trashed documents that are pending deletion." i18n-info infoLink="usage/#document-trash"> - - - -
    - +
    @@ -28,7 +28,7 @@
    - +
    @@ -38,7 +38,7 @@ - @if (loading) { + @if (loading()) {
    @@ -46,11 +46,11 @@ } - @for (document of documentsInTrash; track document.id) { - + @for (document of documentsInTrash(); track document.id) { +
    - +
    @@ -88,16 +88,16 @@
    -@if (!loading) { +@if (!loading()) {
    - {totalDocuments, plural, =1 {One document in trash} other {{{totalDocuments || 0}} total documents in trash}} - @if (selectedDocuments.size > 0) { -  ({{selectedDocuments.size}} selected) + {totalDocuments(), plural, =1 {One document in trash} other {{{totalDocuments() || 0}} total documents in trash}} + @if (selectedDocuments().size > 0) { +  ({{selectedDocuments().size}} selected) }
    - @if (documentsInTrash.length > 20) { - + @if (documentsInTrash().length > 20) { + }
    } diff --git a/src-ui/src/app/components/admin/trash/trash.component.spec.ts b/src-ui/src/app/components/admin/trash/trash.component.spec.ts index 215b0b253..7edb7121e 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.spec.ts +++ b/src-ui/src/app/components/admin/trash/trash.component.spec.ts @@ -60,10 +60,15 @@ describe('TrashComponent', () => { modalService = TestBed.inject(NgbModal) toastService = TestBed.inject(ToastService) router = TestBed.inject(Router) + jest.spyOn(router, 'navigate').mockResolvedValue(true) component = fixture.componentInstance fixture.detectChanges() }) + afterEach(() => { + jest.useRealTimers() + }) + it('should call correct service method on reload', () => { jest.useFakeTimers() const trashSpy = jest.spyOn(trashService, 'getTrash') @@ -77,7 +82,7 @@ describe('TrashComponent', () => { component.reload() jest.advanceTimersByTime(100) expect(trashSpy).toHaveBeenCalled() - expect(component.documentsInTrash).toEqual(documentsInTrash) + expect(component.documentsInTrash()).toEqual(documentsInTrash) }) it('should support delete document, show error if needed', () => { @@ -179,8 +184,8 @@ describe('TrashComponent', () => { }) it('should support toggle all items in view', () => { - component.documentsInTrash = documentsInTrash - expect(component.selectedDocuments.size).toEqual(0) + component.documentsInTrash.set(documentsInTrash) + expect(component.selectedDocuments().size).toEqual(0) const toggleAllSpy = jest.spyOn(component, 'toggleAll') const checkButton = fixture.debugElement.queryAll( By.css('input.form-check-input') @@ -189,21 +194,21 @@ describe('TrashComponent', () => { checkButton.nativeElement.checked = true checkButton.nativeElement.dispatchEvent(new Event('click')) expect(toggleAllSpy).toHaveBeenCalled() - expect(component.selectedDocuments.size).toEqual(documentsInTrash.length) + expect(component.selectedDocuments().size).toEqual(documentsInTrash.length) }) it('should support toggle item', () => { - component.selectedDocuments = new Set([1]) + component.selectedDocuments.set(new Set([1])) component.toggleSelected(documentsInTrash[0]) - expect(component.selectedDocuments.size).toEqual(0) + expect(component.selectedDocuments().size).toEqual(0) component.toggleSelected(documentsInTrash[0]) - expect(component.selectedDocuments.size).toEqual(1) + expect(component.selectedDocuments().size).toEqual(1) }) it('should support clear selection', () => { - component.selectedDocuments = new Set([1]) + component.selectedDocuments.set(new Set([1])) component.clearSelection() - expect(component.selectedDocuments.size).toEqual(0) + expect(component.selectedDocuments().size).toEqual(0) }) it('should correctly display days remaining', () => { diff --git a/src-ui/src/app/components/admin/trash/trash.component.ts b/src-ui/src/app/components/admin/trash/trash.component.ts index dce6a6e88..68b3464eb 100644 --- a/src-ui/src/app/components/admin/trash/trash.component.ts +++ b/src-ui/src/app/components/admin/trash/trash.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, inject } from '@angular/core' +import { Component, OnDestroy, inject, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { Router } from '@angular/router' import { @@ -7,7 +7,7 @@ import { NgbPaginationModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { delay, takeUntil, tap } from 'rxjs' +import { takeUntil, tap } from 'rxjs' import { Document } from 'src/app/data/document' import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { SettingsService } from 'src/app/services/settings.service' @@ -42,11 +42,11 @@ export class TrashComponent private settingsService = inject(SettingsService) private router = inject(Router) - public documentsInTrash: Document[] = [] - public selectedDocuments: Set = new Set() - public allToggled: boolean = false - public page: number = 1 - public totalDocuments: number + readonly documentsInTrash = signal([]) + readonly selectedDocuments = signal>(new Set()) + readonly allToggled = signal(false) + readonly page = signal(1) + readonly totalDocuments = signal(undefined) constructor() { super() @@ -54,20 +54,19 @@ export class TrashComponent } reload() { - this.loading = true + this.loading.set(true) this.trashService - .getTrash(this.page) + .getTrash(this.page()) .pipe( tap((r) => { - this.documentsInTrash = r.results - this.totalDocuments = r.count - this.selectedDocuments.clear() - this.loading = false - }), - delay(100) + this.documentsInTrash.set(r.results) + this.totalDocuments.set(r.count) + this.selectedDocuments.set(new Set()) + this.loading.set(false) + }) ) .subscribe(() => { - this.show = true + this.show.set(true) }) } @@ -122,7 +121,7 @@ export class TrashComponent .subscribe({ next: () => { this.toastService.showInfo($localize`Document(s) deleted`) - this.allToggled = false + this.allToggled.set(false) modal.close() this.reload() }, @@ -165,7 +164,7 @@ export class TrashComponent .subscribe({ next: () => { this.toastService.showInfo($localize`Document(s) restored`) - this.allToggled = false + this.allToggled.set(false) this.reload() }, error: (err) => { @@ -179,24 +178,29 @@ export class TrashComponent toggleAll(event: PointerEvent) { if ((event.target as HTMLInputElement).checked) { - this.selectedDocuments = new Set(this.documentsInTrash.map((t) => t.id)) + this.selectedDocuments.set( + new Set(this.documentsInTrash().map((t) => t.id)) + ) } else { this.clearSelection() } } toggleSelected(object: Document) { - this.selectedDocuments.has(object.id) - ? this.selectedDocuments.delete(object.id) - : this.selectedDocuments.add(object.id) + const selectedDocuments = new Set(this.selectedDocuments()) + selectedDocuments.has(object.id) + ? selectedDocuments.delete(object.id) + : selectedDocuments.add(object.id) + this.selectedDocuments.set(selectedDocuments) } clearSelection() { - this.allToggled = false - this.selectedDocuments.clear() + this.allToggled.set(false) + this.selectedDocuments.set(new Set()) } getDaysRemaining(document: Document): number { + this.settingsService.trackChanges() const delay = this.settingsService.get(SETTINGS_KEYS.EMPTY_TRASH_DELAY) const diff = new Date().getTime() - new Date(document.deleted_at).getTime() const days = Math.ceil(diff / (1000 * 3600 * 24)) diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.html b/src-ui/src/app/components/admin/users-groups/users-groups.component.html index a13bb6633..cdabad926 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.html +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.html @@ -7,7 +7,7 @@ > -@if (canViewUsers && users) { +@if (canViewUsers && users()) {

    Users

    - @for (user of users; track user) { + @for (user of users(); track user) {
  • @@ -45,7 +45,7 @@
} -@if (canViewGroups && groups) { +@if (canViewGroups && groups()) {

Groups @@ -80,13 +80,13 @@ } - @if (groups.length === 0) { + @if (groups().length === 0) {
  • No groups defined
  • } } -@if ((canViewUsers && !users) || (canViewGroups && !groups)) { +@if ((canViewUsers && !users()) || (canViewGroups && !groups())) {
    Loading...
    diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts index 20566a9b0..a06852994 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.spec.ts @@ -1,12 +1,7 @@ import { DatePipe } from '@angular/common' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { of, throwError } from 'rxjs' @@ -57,7 +52,7 @@ describe('UsersAndGroupsComponent', () => { }).compileComponents() fixture = TestBed.createComponent(UsersAndGroupsComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = users[0] + settingsService.currentUser.set(users[0]) userService = TestBed.inject(UserService) modalService = TestBed.inject(NgbModal) toastService = TestBed.inject(ToastService) @@ -109,7 +104,7 @@ describe('UsersAndGroupsComponent', () => { const toastInfoSpy = jest.spyOn(toastService, 'showInfo') editDialog.failed.emit() expect(toastErrorSpy).toHaveBeenCalled() - settingsService.currentUser = users[1] // simulate logged in as different user + settingsService.currentUser.set(users[1]) // simulate logged in as different user editDialog.succeeded.emit(users[0]) expect(toastInfoSpy).toHaveBeenCalledWith( `Saved user "${users[0].username}".` @@ -138,7 +133,8 @@ describe('UsersAndGroupsComponent', () => { expect(toastInfoSpy).toHaveBeenCalledWith('Deleted user "user1"') }) - it('should logout current user if password changed, after delay', fakeAsync(() => { + it('should logout current user if password changed, after delay', () => { + jest.useFakeTimers() completeSetup() let modal: NgbModalRef modalService.activeInstances.subscribe((refs) => (modal = refs[0])) @@ -148,14 +144,15 @@ describe('UsersAndGroupsComponent', () => { .mockImplementation(() => {}) const editDialog = modal.componentInstance as UserEditDialogComponent editDialog.passwordIsSet = true - settingsService.currentUser = users[0] // simulate logged in as same user + settingsService.currentUser.set(users[0]) // simulate logged in as same user editDialog.succeeded.emit(users[0]) fixture.detectChanges() - tick(2600) + jest.advanceTimersByTime(2600) expect(navSpy).toHaveBeenCalledWith( `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/` ) - })) + jest.useRealTimers() + }) it('should support edit / create group, show error if needed', () => { completeSetup() diff --git a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts index 960b2e0c4..252c60640 100644 --- a/src-ui/src/app/components/admin/users-groups/users-groups.component.ts +++ b/src-ui/src/app/components/admin/users-groups/users-groups.component.ts @@ -1,4 +1,4 @@ -import { Component, OnDestroy, OnInit, inject } from '@angular/core' +import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { Subject, first, takeUntil } from 'rxjs' @@ -43,8 +43,8 @@ export class UsersAndGroupsComponent permissionsService = inject(PermissionsService) private settings = inject(SettingsService) - users: User[] - groups: Group[] + readonly users = signal(null) + readonly groups = signal(null) unsubscribeNotifier: Subject = new Subject() @@ -69,7 +69,7 @@ export class UsersAndGroupsComponent .pipe(first(), takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (r) => { - this.users = r.results + this.users.set(r.results) }, error: (e) => { this.toastService.showError($localize`Error retrieving users`, e) @@ -83,7 +83,7 @@ export class UsersAndGroupsComponent .pipe(first(), takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (r) => { - this.groups = r.results + this.groups.set(r.results) }, error: (e) => { this.toastService.showError($localize`Error retrieving groups`, e) @@ -101,15 +101,15 @@ export class UsersAndGroupsComponent backdrop: 'static', size: 'xl', }) - modal.componentInstance.dialogMode = user - ? EditDialogMode.EDIT - : EditDialogMode.CREATE + modal.componentInstance.dialogMode.set( + user ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) modal.componentInstance.object = user modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((newUser: User) => { if ( - newUser.id === this.settings.currentUser.id && + newUser.id === this.settings.currentUser().id && (modal.componentInstance as UserEditDialogComponent).passwordIsSet ) { this.toastService.showInfo( @@ -125,7 +125,7 @@ export class UsersAndGroupsComponent $localize`Saved user "${newUser.username}".` ) this.usersService.listAll().subscribe((r) => { - this.users = r.results + this.users.set(r.results) }) } }) @@ -152,7 +152,7 @@ export class UsersAndGroupsComponent modal.close() this.toastService.showInfo($localize`Deleted user "${user.username}"`) this.usersService.listAll().subscribe((r) => { - this.users = r.results + this.users.set(r.results) }) }, error: (e) => { @@ -170,16 +170,16 @@ export class UsersAndGroupsComponent backdrop: 'static', size: 'lg', }) - modal.componentInstance.dialogMode = group - ? EditDialogMode.EDIT - : EditDialogMode.CREATE + modal.componentInstance.dialogMode.set( + group ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) modal.componentInstance.object = group modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((newGroup) => { this.toastService.showInfo($localize`Saved group "${newGroup.name}".`) this.groupsService.listAll().subscribe((r) => { - this.groups = r.results + this.groups.set(r.results) }) }) modal.componentInstance.failed @@ -205,7 +205,7 @@ export class UsersAndGroupsComponent modal.close() this.toastService.showInfo($localize`Deleted group "${group.name}"`) this.groupsService.listAll().subscribe((r) => { - this.groups = r.results + this.groups.set(r.results) }) }, error: (e) => { @@ -219,6 +219,6 @@ export class UsersAndGroupsComponent } getGroupName(id: number): string { - return this.groups?.find((g) => g.id === id)?.name ?? '' + return this.groups()?.find((g) => g.id === id)?.name ?? '' } } diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 5d50768e9..469b55472 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -1,7 +1,7 @@
    + [class.mobile-hidden]="mobileSearchHidden()">
    @@ -68,8 +68,8 @@
    + [ngClass]="slimSidebarEnabled ? 'slim' : 'col-md-3 col-lg-2 col-xxxl-1'" [class.animating]="slimSidebarAnimating()" + [ngbCollapse]="isMenuCollapsed()"> @if (canSaveSettings) {

      @for (view of savedViewService.sidebarViews; track view.id) { -
    • {{ savedViewService.getDocumentCount(view) }} } - @if (settingsService.organizingSidebarSavedViews) { + @if (settingsService.organizingSidebarSavedViews()) {
      @@ -337,10 +337,10 @@ {{ versionString }} - @if (!settingsService.updateCheckingIsSet || appRemoteVersion) { + @if (!settingsService.updateCheckingIsSet || appRemoteVersion()) {
      - Paperless-ngx {{ appRemoteVersion.version }} is + Paperless-ngx {{ appRemoteVersion().version }} is available.
      Click to view.
      @@ -358,13 +358,13 @@

      @if (settingsService.updateCheckingIsSet) { - @if (appRemoteVersion.update_available) { + @if (appRemoteVersion().update_available) { - @if (appRemoteVersion?.update_available) { + @if (appRemoteVersion()?.update_available) { Update available } @@ -385,7 +385,7 @@
      -
      diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts index d6f84276f..71b22bef1 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts @@ -4,12 +4,7 @@ import { HttpTestingController, provideHttpClientTesting, } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { BrowserModule } from '@angular/platform-browser' import { ActivatedRoute, Router } from '@angular/router' @@ -244,11 +239,12 @@ describe('AppFrameComponent', () => { expect(toastSpy).toHaveBeenCalled() }) - it('should support toggling slim sidebar and saving', fakeAsync(() => { + it('should support toggling slim sidebar and saving', () => { + jest.useFakeTimers() const saveSettingSpy = jest.spyOn(settingsService, 'set') settingsService.set(SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED, []) expect(component.slimSidebarEnabled).toBeFalsy() - expect(component.slimSidebarAnimating).toBeFalsy() + expect(component.slimSidebarAnimating()).toBeFalsy() component.toggleSlimSidebar() const requests = httpTestingController.match( `${environment.apiBaseUrl}ui_settings/` @@ -259,9 +255,9 @@ describe('AppFrameComponent', () => { requests[0].request.body.settings.attributes_sections_collapsed ).toEqual(['attributes']) requests[0].flush({ success: true }) - expect(component.slimSidebarAnimating).toBeTruthy() - tick(200) - expect(component.slimSidebarAnimating).toBeFalsy() + expect(component.slimSidebarAnimating()).toBeTruthy() + jest.advanceTimersByTime(200) + expect(component.slimSidebarAnimating()).toBeFalsy() expect(component.slimSidebarEnabled).toBeTruthy() expect(saveSettingSpy).toHaveBeenCalledWith( SETTINGS_KEYS.SLIM_SIDEBAR, @@ -271,7 +267,8 @@ describe('AppFrameComponent', () => { SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED, ['attributes'] ) - })) + jest.useRealTimers() + }) it('should show error on toggle slim sidebar if store settings fails', () => { jest.spyOn(console, 'warn').mockImplementation(() => {}) @@ -291,9 +288,9 @@ describe('AppFrameComponent', () => { fixture.nativeElement as HTMLDivElement ).querySelector('button[data-toggle=collapse]') button.dispatchEvent(new MouseEvent('click')) - expect(component.isMenuCollapsed).toBeFalsy() + expect(component.isMenuCollapsed()).toBeFalsy() component.closeMenu() - expect(component.isMenuCollapsed).toBeTruthy() + expect(component.isMenuCollapsed()).toBeTruthy() }) it('should hide mobile search when scrolling down and show it when scrolling up', () => { @@ -308,14 +305,14 @@ describe('AppFrameComponent', () => { value: 40, }) component.onWindowScroll() - expect(component.mobileSearchHidden).toBe(true) + expect(component.mobileSearchHidden()).toBe(true) Object.defineProperty(globalThis, 'scrollY', { configurable: true, value: 0, }) component.onWindowScroll() - expect(component.mobileSearchHidden).toBe(false) + expect(component.mobileSearchHidden()).toBe(false) }) it('should keep mobile search visible on desktop scroll or resize', () => { @@ -323,13 +320,13 @@ describe('AppFrameComponent', () => { value: 1024, }) component.ngOnInit() - component.mobileSearchHidden = true + component.mobileSearchHidden.set(true) component.onWindowScroll() - expect(component.mobileSearchHidden).toBe(false) + expect(component.mobileSearchHidden()).toBe(false) - component.mobileSearchHidden = true + component.mobileSearchHidden.set(true) component.onWindowResize() }) @@ -338,7 +335,7 @@ describe('AppFrameComponent', () => { value: 767, }) component.ngOnInit() - component.isMenuCollapsed = false + component.isMenuCollapsed.set(false) Object.defineProperty(globalThis, 'scrollY', { configurable: true, @@ -346,7 +343,7 @@ describe('AppFrameComponent', () => { }) component.onWindowScroll() - expect(component.mobileSearchHidden).toBe(false) + expect(component.mobileSearchHidden()).toBe(false) }) it('should support close document & navigate on close current doc', () => { @@ -379,11 +376,11 @@ describe('AppFrameComponent', () => { }) it('should disable global dropzone on start drag + drop, re-enable after', () => { - expect(settingsService.globalDropzoneEnabled).toBeTruthy() + expect(settingsService.globalDropzoneEnabled()).toBeTruthy() component.onDragStart(null) - expect(settingsService.globalDropzoneEnabled).toBeFalsy() + expect(settingsService.globalDropzoneEnabled()).toBeFalsy() component.onDragEnd(null) - expect(settingsService.globalDropzoneEnabled).toBeTruthy() + expect(settingsService.globalDropzoneEnabled()).toBeTruthy() }) it('should update saved view sorting on drag + drop, show info', () => { diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index 7989eb3e1..db752dde7 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -6,7 +6,7 @@ import { moveItemInArray, } from '@angular/cdk/drag-drop' import { NgClass } from '@angular/common' -import { Component, HostListener, inject, OnInit } from '@angular/core' +import { Component, HostListener, inject, OnInit, signal } from '@angular/core' import { ActivatedRoute, Router, RouterModule } from '@angular/router' import { NgbCollapseModule, @@ -90,14 +90,10 @@ export class AppFrameComponent permissionsService = inject(PermissionsService) private djangoMessagesService = inject(DjangoMessagesService) - appRemoteVersion: AppRemoteVersion - - isMenuCollapsed: boolean = true - - slimSidebarAnimating: boolean = false - - public mobileSearchHidden: boolean = false - + readonly appRemoteVersion = signal(null) + readonly isMenuCollapsed = signal(true) + readonly slimSidebarAnimating = signal(false) + readonly mobileSearchHidden = signal(false) private lastScrollY: number = 0 constructor() { @@ -147,7 +143,7 @@ export class AppFrameComponent } toggleSlimSidebar(): void { - this.slimSidebarAnimating = true + this.slimSidebarAnimating.set(true) const slimSidebarEnabled = !this.slimSidebarEnabled this.settingsService.set(SETTINGS_KEYS.SLIM_SIDEBAR, slimSidebarEnabled) if (slimSidebarEnabled) { @@ -167,7 +163,7 @@ export class AppFrameComponent }, }) setTimeout(() => { - this.slimSidebarAnimating = false + this.slimSidebarAnimating.set(false) }, 200) // slightly longer than css animation for slim sidebar } @@ -177,11 +173,25 @@ export class AppFrameComponent this.attributesSectionsCollapsed = !this.attributesSectionsCollapsed } + toggleMenuCollapsed(): void { + this.isMenuCollapsed.set(!this.isMenuCollapsed()) + } + + closeMobileSearch(): void { + this.mobileSearchHidden.set(false) + } + + setMobileSearchHidden(hidden: boolean): void { + this.mobileSearchHidden.set(hidden) + } + get versionString(): string { + this.settingsService.trackChanges() return `${environment.appTitle} v${this.settingsService.get(SETTINGS_KEYS.VERSION)}${environment.tag === 'prod' ? '' : ` #${environment.tag}`}` } get customAppTitle(): string { + this.settingsService.trackChanges() return this.settingsService.get(SETTINGS_KEYS.APP_TITLE) } @@ -224,6 +234,7 @@ export class AppFrameComponent } get slimSidebarEnabled(): boolean { + this.settingsService.trackChanges() return this.settingsService.get(SETTINGS_KEYS.SLIM_SIDEBAR) } @@ -243,6 +254,7 @@ export class AppFrameComponent } get attributesSectionsCollapsed(): boolean { + this.settingsService.trackChanges() return this.settingsService .get(SETTINGS_KEYS.ATTRIBUTES_SECTIONS_COLLAPSED) ?.includes(CollapsibleSection.ATTRIBUTES) @@ -268,13 +280,14 @@ export class AppFrameComponent } get aiEnabled(): boolean { + this.settingsService.trackChanges() return this.settingsService.get(SETTINGS_KEYS.AI_ENABLED) } @HostListener('window:resize') onWindowResize(): void { if (!this.isMobileViewport()) { - this.mobileSearchHidden = false + this.mobileSearchHidden.set(false) } } @@ -282,8 +295,8 @@ export class AppFrameComponent onWindowScroll(): void { const currentScrollY = window.scrollY - if (!this.isMobileViewport() || this.isMenuCollapsed === false) { - this.mobileSearchHidden = false + if (!this.isMobileViewport() || this.isMenuCollapsed() === false) { + this.mobileSearchHidden.set(false) this.lastScrollY = currentScrollY return } @@ -291,9 +304,9 @@ export class AppFrameComponent const delta = currentScrollY - this.lastScrollY if (currentScrollY <= 0 || delta < -SCROLL_THRESHOLD) { - this.mobileSearchHidden = false + this.mobileSearchHidden.set(false) } else if (currentScrollY > SCROLL_THRESHOLD && delta > SCROLL_THRESHOLD) { - this.mobileSearchHidden = true + this.mobileSearchHidden.set(true) } this.lastScrollY = currentScrollY @@ -304,7 +317,7 @@ export class AppFrameComponent } closeMenu() { - this.isMenuCollapsed = true + this.isMenuCollapsed.set(true) } editProfile() { @@ -367,11 +380,11 @@ export class AppFrameComponent } onDragStart(event: CdkDragStart) { - this.settingsService.globalDropzoneEnabled = false + this.settingsService.globalDropzoneEnabled.set(false) } onDragEnd(event: CdkDragEnd) { - this.settingsService.globalDropzoneEnabled = true + this.settingsService.globalDropzoneEnabled.set(true) } onDrop(event: CdkDragDrop) { @@ -392,7 +405,7 @@ export class AppFrameComponent this.remoteVersionService .checkForUpdates() .subscribe((appRemoteVersion: AppRemoteVersion) => { - this.appRemoteVersion = appRemoteVersion + this.appRemoteVersion.set(appRemoteVersion) }) } @@ -419,9 +432,10 @@ export class AppFrameComponent } get showSidebarCounts(): boolean { + this.settingsService.trackChanges() return ( this.settingsService.get(SETTINGS_KEYS.SIDEBAR_VIEWS_SHOW_COUNT) && - !this.settingsService.organizingSidebarSavedViews + !this.settingsService.organizingSidebarSavedViews() ) } } diff --git a/src-ui/src/app/components/app-frame/global-search/global-search.component.html b/src-ui/src/app/components/app-frame/global-search/global-search.component.html index 5fdf8b9a7..06c77ac3d 100644 --- a/src-ui/src/app/components/app-frame/global-search/global-search.component.html +++ b/src-ui/src/app/components/app-frame/global-search/global-search.component.html @@ -8,17 +8,17 @@ placeholder="Search" aria-label="Search" i18n-placeholder autocomplete="off" spellcheck="false" - [(ngModel)]="query" - (ngModelChange)="this.queryDebounce.next($event)" + [ngModel]="query()" + (ngModelChange)="queryDebounce.next($event)" (keydown)="searchInputKeyDown($event)" ngbDropdownAnchor>
      - @if (loading) { + @if (loading()) {
      }
      - @if (query) { + @if (query()) { - @if (toasts.length === 0) { + @if (toasts().length === 0) {

      No notifications

      }
      - @for (toast of toasts; track toast.id) { + @for (toast of toasts(); track toast.id) { }
      diff --git a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts index 33b948f30..42a891642 100644 --- a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts +++ b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.spec.ts @@ -1,12 +1,6 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - discardPeriodicTasks, - fakeAsync, - flush, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { Subject } from 'rxjs' import { Toast, ToastService } from 'src/app/services/toast.service' @@ -42,7 +36,8 @@ describe('ToastsDropdownComponent', () => { let component: ToastsDropdownComponent let fixture: ComponentFixture let toastService: ToastService - let toastsSubject: Subject = new Subject() + let toastsSubject: Subject + let getToastsSpy: jest.SpyInstance beforeEach(async () => { TestBed.configureTestingModule({ @@ -56,57 +51,43 @@ describe('ToastsDropdownComponent', () => { ], }).compileComponents() - fixture = TestBed.createComponent(ToastsDropdownComponent) toastService = TestBed.inject(ToastService) - jest.spyOn(toastService, 'getToasts').mockReturnValue(toastsSubject) + toastsSubject = new Subject() + getToastsSpy = jest + .spyOn(toastService, 'getToasts') + .mockReturnValue(toastsSubject) + fixture = TestBed.createComponent(ToastsDropdownComponent) component = fixture.componentInstance fixture.detectChanges() }) - it('should call getToasts and return toasts', fakeAsync(() => { - const spy = jest.spyOn(toastService, 'getToasts') - - component.ngOnInit() + it('should call getToasts and return toasts', () => { toastsSubject.next(toasts) fixture.detectChanges() - expect(spy).toHaveBeenCalled() - expect(component.toasts).toContainEqual({ + expect(getToastsSpy).toHaveBeenCalled() + expect(component.toasts()).toContainEqual({ id: 'abc-123', content: 'foo bar', delay: 5000, }) + }) - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should show a toast', fakeAsync(() => { - component.ngOnInit() + it('should show a toast', () => { toastsSubject.next(toasts) fixture.detectChanges() expect(fixture.nativeElement.textContent).toContain('foo bar') + }) - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) - - it('should toggle suppressPopupToasts', fakeAsync((finish) => { - component.ngOnInit() + it('should toggle suppressPopupToasts', () => { fixture.detectChanges() toastsSubject.next(toasts) const spy = jest.spyOn(toastService, 'suppressPopupToasts', 'set') component.onOpenChange(true) expect(spy).toHaveBeenCalledWith(true) - - component.ngOnDestroy() - flush() - discardPeriodicTasks() - })) + }) }) diff --git a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts index 7274f2b53..b0bcafd68 100644 --- a/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts +++ b/src-ui/src/app/components/app-frame/toasts-dropdown/toasts-dropdown.component.ts @@ -1,10 +1,11 @@ -import { Component, OnDestroy, OnInit, inject } from '@angular/core' +import { Component, inject } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { NgbDropdownModule, NgbProgressbarModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Subscription } from 'rxjs' +import { map } from 'rxjs' import { Toast, ToastService } from 'src/app/services/toast.service' import { ToastComponent } from '../../common/toast/toast.component' @@ -19,22 +20,13 @@ import { ToastComponent } from '../../common/toast/toast.component' NgxBootstrapIconsModule, ], }) -export class ToastsDropdownComponent implements OnInit, OnDestroy { +export class ToastsDropdownComponent { toastService = inject(ToastService) - private subscription: Subscription - - public toasts: Toast[] = [] - - ngOnDestroy(): void { - this.subscription?.unsubscribe() - } - - ngOnInit(): void { - this.subscription = this.toastService.getToasts().subscribe((toasts) => { - this.toasts = [...toasts] - }) - } + readonly toasts = toSignal( + this.toastService.getToasts().pipe(map((toasts) => [...toasts])), + { initialValue: [] as Toast[] } + ) onOpenChange(open: boolean): void { this.toastService.suppressPopupToasts = open diff --git a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.html b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.html index 831bb1b55..7292ba8ea 100644 --- a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.html +++ b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.html @@ -1,10 +1,10 @@ -@if (active) { +@if (active()) { diff --git a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.spec.ts b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.spec.ts index 2f45dd902..50cb5c3bb 100644 --- a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.spec.ts +++ b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.spec.ts @@ -21,20 +21,20 @@ describe('ClearableBadgeComponent', () => { }) it('should support selected', () => { - component.selected = true - expect(component.active).toBeTruthy() + fixture.componentRef.setInput('selected', true) + expect(component.active()).toBeTruthy() }) it('should support numbered', () => { - component.number = 3 + fixture.componentRef.setInput('number', 3) fixture.detectChanges() - expect(component.active).toBeTruthy() + expect(component.active()).toBeTruthy() expect((fixture.nativeElement as HTMLDivElement).textContent).toContain('3') }) it('should support selected', () => { let clearedResult - component.selected = true + fixture.componentRef.setInput('selected', true) fixture.detectChanges() component.cleared.subscribe((clear) => { clearedResult = clear diff --git a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.ts b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.ts index 51ae812c7..0ee366110 100644 --- a/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.ts +++ b/src-ui/src/app/components/common/clearable-badge/clearable-badge.component.ts @@ -1,4 +1,4 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core' +import { Component, computed, EventEmitter, input, Output } from '@angular/core' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @Component({ @@ -8,24 +8,15 @@ import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' imports: [NgxBootstrapIconsModule], }) export class ClearableBadgeComponent { - constructor() {} - - @Input() - number: number - - @Input() - selected: boolean + readonly number = input(undefined) + readonly selected = input(undefined) @Output() cleared: EventEmitter = new EventEmitter() - get active(): boolean { - return this.selected || this.number > -1 - } + readonly active = computed(() => this.selected() || this.number() > -1) - get isNumbered(): boolean { - return this.number > -1 - } + readonly isNumbered = computed(() => this.number() > -1) onClick(event: PointerEvent) { this.cleared.emit(true) diff --git a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts index 7ef9ccce0..da7149673 100644 --- a/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts +++ b/src-ui/src/app/components/common/confirm-dialog/confirm-dialog.component.ts @@ -23,10 +23,10 @@ export class ConfirmDialogComponent extends LoadingComponentWithPermissions { title = $localize`Confirmation` @Input() - messageBold + messageBold: string @Input() - message + message: string @Input() btnClass = 'btn-primary' @@ -38,7 +38,7 @@ export class ConfirmDialogComponent extends LoadingComponentWithPermissions { alternativeBtnClass = 'btn-secondary' @Input() - alternativeBtnCaption + alternativeBtnCaption: string @Input() cancelBtnClass = 'btn-outline-secondary' diff --git a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html index ab0951d54..26f0464bf 100644 --- a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html +++ b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.html @@ -9,9 +9,9 @@
        - @for (documentID of documentIDs; track documentID) { + @for (documentID of documentIDs(); track documentID) { @let document = getDocument(documentID); @if (document) {
      • @@ -36,22 +36,22 @@
        - - @for (document of documents; track document.id) { + @for (document of documents(); track document.id) { }
        - +
        - +
        - @if (!archiveFallback) { + @if (!archiveFallback()) {

        Note that only PDFs will be included.

        } diff --git a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.spec.ts b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.spec.ts index db772c592..e9b99763a 100644 --- a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.spec.ts @@ -50,12 +50,12 @@ describe('MergeConfirmDialogComponent', () => { component.ngOnInit() - expect(component.documents).toEqual(documents) - expect(documentService.getFew).toHaveBeenCalledWith(component.documentIDs) + expect(component.documents()).toEqual(documents) + expect(documentService.getFew).toHaveBeenCalledWith(component.documentIDs()) }) it('should move documentIDs on drop', () => { - component.documentIDs = [1, 2, 3] + component.documentIDs.set([1, 2, 3]) const event = { previousIndex: 1, currentIndex: 2, @@ -63,7 +63,7 @@ describe('MergeConfirmDialogComponent', () => { component.onDrop(event as any) - expect(component.documentIDs).toEqual([1, 3, 2]) + expect(component.documentIDs()).toEqual([1, 3, 2]) }) it('should get document by ID', () => { diff --git a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.ts index dbf269a23..e852e5a93 100644 --- a/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.ts +++ b/src-ui/src/app/components/common/confirm-dialog/merge-confirm-dialog/merge-confirm-dialog.component.ts @@ -4,7 +4,7 @@ import { moveItemInArray, } from '@angular/cdk/drag-drop' import { AsyncPipe } from '@angular/common' -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, inject, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { takeUntil } from 'rxjs' @@ -36,15 +36,11 @@ export class MergeConfirmDialogComponent private documentService = inject(DocumentService) private permissionService = inject(PermissionsService) - public documentIDs: number[] = [] - public archiveFallback: boolean = false - public deleteOriginals: boolean = false - private _documents: Document[] = [] - get documents(): Document[] { - return this._documents - } - - public metadataDocumentID: number = -1 + readonly documentIDs = signal([]) + readonly archiveFallback = signal(false) + readonly deleteOriginals = signal(false) + readonly documents = signal([]) + readonly metadataDocumentID = signal(-1) constructor() { super() @@ -52,23 +48,25 @@ export class MergeConfirmDialogComponent ngOnInit() { this.documentService - .getFew(this.documentIDs) + .getFew(this.documentIDs()) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((r) => { - this._documents = r.results + this.documents.set(r.results) }) } onDrop(event: CdkDragDrop) { - moveItemInArray(this.documentIDs, event.previousIndex, event.currentIndex) + const documentIDs = this.documentIDs().concat() + moveItemInArray(documentIDs, event.previousIndex, event.currentIndex) + this.documentIDs.set(documentIDs) } getDocument(documentID: number): Document { - return this.documents.find((d) => d.id === documentID) + return this.documents().find((d) => d.id === documentID) } get userOwnsAllDocuments(): boolean { - return this.documents.every((d) => + return this.documents().every((d) => this.permissionService.currentUserOwnsObject(d) ) } diff --git a/src-ui/src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts index 82444ad13..28e78fc88 100644 --- a/src-ui/src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts +++ b/src-ui/src/app/components/common/confirm-dialog/password-removal-confirm-dialog/password-removal-confirm-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core' +import { Component } from '@angular/core' import { FormsModule } from '@angular/forms' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { ConfirmDialogComponent } from '../confirm-dialog.component' @@ -14,18 +14,11 @@ export class PasswordRemovalConfirmDialogComponent extends ConfirmDialogComponen includeMetadata: boolean = true deleteOriginal: boolean = false - @Input() - override title = $localize`Remove password protection` - - @Input() - override message = - $localize`Create an unprotected copy or replace the existing file.` - - @Input() - override btnCaption = $localize`Start` - constructor() { super() + this.title = $localize`Remove password protection` + this.message = $localize`Create an unprotected copy or replace the existing file.` + this.btnCaption = $localize`Start` } onUpdateDocumentChange(updateDocument: boolean) { diff --git a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html index ebd5b1fa8..8f8ecf1f1 100644 --- a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html +++ b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.html @@ -11,8 +11,8 @@
        - @if (documentID) { - + @if (documentID()) { + }
        @@ -21,7 +21,7 @@
        - @if (showPDFNote) { + @if (showPDFNote()) {

        Note that only PDFs will be rotated.

        } diff --git a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.spec.ts b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.spec.ts index f33f2638c..6e0bca287 100644 --- a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.spec.ts @@ -28,7 +28,7 @@ describe('RotateConfirmDialogComponent', () => { }) it('should support rotating the image', () => { - component.documentID = 1 + component.documentID.set(1) fixture.detectChanges() component.rotate() fixture.detectChanges() diff --git a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.ts b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.ts index 90d759da3..18d844ef9 100644 --- a/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.ts +++ b/src-ui/src/app/components/common/confirm-dialog/rotate-confirm-dialog/rotate-confirm-dialog.component.ts @@ -1,5 +1,5 @@ import { NgStyle } from '@angular/common' -import { Component, inject } from '@angular/core' +import { Component, inject, signal } from '@angular/core' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { DocumentService } from 'src/app/services/rest/document.service' import { ConfirmDialogComponent } from '../confirm-dialog.component' @@ -13,14 +13,12 @@ import { ConfirmDialogComponent } from '../confirm-dialog.component' export class RotateConfirmDialogComponent extends ConfirmDialogComponent { documentService = inject(DocumentService) - public documentID: number - public showPDFNote: boolean = true - - // animation is better if we dont normalize yet - public rotation: number = 0 + readonly documentID = signal(undefined) + readonly showPDFNote = signal(true) + readonly rotation = signal(0) public get degrees(): number { - let degrees = this.rotation % 360 + let degrees = this.rotation() % 360 if (degrees < 0) degrees += 360 return degrees } @@ -30,6 +28,6 @@ export class RotateConfirmDialogComponent extends ConfirmDialogComponent { } rotate(clockwise: boolean = true) { - this.rotation += clockwise ? 90 : -90 + this.rotation.update((rotation) => rotation + (clockwise ? 90 : -90)) } } diff --git a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts index 78df9c74f..a0d954a03 100644 --- a/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/custom-fields-dropdown/custom-fields-dropdown.component.spec.ts @@ -1,11 +1,6 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { By } from '@angular/platform-browser' import { @@ -74,7 +69,7 @@ describe('CustomFieldsDropdownComponent', () => { }) ) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1, username: 'test' } + settingsService.currentUser.set({ id: 1, username: 'test' }) fixture = TestBed.createComponent(CustomFieldsDropdownComponent) component = fixture.componentInstance fixture.detectChanges() @@ -110,7 +105,8 @@ describe('CustomFieldsDropdownComponent', () => { ) }) - it('should support creating field, show error if necessary, then add', fakeAsync(() => { + it('should support creating field, show error if necessary, then add', () => { + jest.useFakeTimers() let modal: NgbModalRef modalService.activeInstances.subscribe((m) => (modal = m[m.length - 1])) const toastErrorSpy = jest.spyOn(toastService, 'showError') @@ -134,11 +130,12 @@ describe('CustomFieldsDropdownComponent', () => { // succeed editDialog.succeeded.emit(fields[0]) - tick(100) + jest.advanceTimersByTime(100) expect(toastInfoSpy).toHaveBeenCalled() expect(getFieldsSpy).toHaveBeenCalled() expect(addFieldSpy).toHaveBeenCalled() - })) + jest.useRealTimers() + }) it('should support creating field with name', () => { let modal: NgbModalRef @@ -150,12 +147,13 @@ describe('CustomFieldsDropdownComponent', () => { expect(editDialog.object.name).toEqual('Foo bar') }) - it('should support arrow keyboard navigation', fakeAsync(() => { + it('should support arrow keyboard navigation', () => { + jest.useFakeTimers() fixture.nativeElement .querySelector('button') .dispatchEvent(new MouseEvent('click')) // open fixture.detectChanges() - tick(100) + jest.advanceTimersByTime(100) const filterInputEl: HTMLInputElement = component.listFilterTextInput.nativeElement expect(document.activeElement).toEqual(filterInputEl) @@ -193,14 +191,16 @@ describe('CustomFieldsDropdownComponent', () => { new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }) ) expect(document.activeElement).toEqual(itemButtons[0]) - })) + jest.useRealTimers() + }) - it('should support arrow keyboard navigation after tab keyboard navigation', fakeAsync(() => { + it('should support arrow keyboard navigation after tab keyboard navigation', () => { + jest.useFakeTimers() fixture.nativeElement .querySelector('button') .dispatchEvent(new MouseEvent('click')) // open fixture.detectChanges() - tick(100) + jest.advanceTimersByTime(100) const filterInputEl: HTMLInputElement = component.listFilterTextInput.nativeElement expect(document.activeElement).toEqual(filterInputEl) @@ -229,9 +229,10 @@ describe('CustomFieldsDropdownComponent', () => { new KeyboardEvent('keydown', { key: 'ArrowDown', bubbles: true }) ) expect(document.activeElement).toEqual(itemButtons[1]) - })) + jest.useRealTimers() + }) - it('should support enter keyboard navigation', fakeAsync(() => { + it('should support enter keyboard navigation', () => { jest.spyOn(component, 'canCreateFields', 'get').mockReturnValue(true) const addFieldSpy = jest.spyOn(component, 'addField') const createFieldSpy = jest.spyOn(component, 'createField') @@ -250,5 +251,5 @@ describe('CustomFieldsDropdownComponent', () => { component.listFilterEnter() expect(createFieldSpy).not.toHaveBeenCalled() expect(addFieldSpy).not.toHaveBeenCalled() - })) + }) }) diff --git a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html index 1c0134f70..724616363 100644 --- a/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html +++ b/src-ui/src/app/components/common/custom-fields-query-dropdown/custom-fields-query-dropdown.component.html @@ -75,7 +75,7 @@
        - { expect(component.name).toBe('test_title') }) - it('should add a default atom on open and focus the select field', fakeAsync(() => { + it('should add a default atom on open', async () => { expect(component.selectionModel.queries.length).toBe(0) component.onOpenChange(true) fixture.detectChanges() - tick() + await fixture.whenStable() expect(component.selectionModel.queries.length).toBe(1) - expect(window.document.activeElement.tagName).toBe('INPUT') - })) + }) describe('CustomFieldQueriesModel', () => { let model: CustomFieldQueriesModel diff --git a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts index ee99a726b..91e06c428 100644 --- a/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/dates-dropdown/dates-dropdown.component.spec.ts @@ -1,12 +1,7 @@ import { DatePipe } from '@angular/common' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' @@ -59,27 +54,32 @@ describe('DatesDropdownComponent', () => { expect(settingsSpy).toHaveBeenCalled() }) - it('should support date input, emit change', fakeAsync(() => { + it('should support date input, emit change', () => { + jest.useFakeTimers() let result: string component.createdDateFromChange.subscribe((date) => (result = date)) const input: HTMLInputElement = fixture.nativeElement.querySelector('input') input.value = '5/30/2023' input.dispatchEvent(new Event('change')) - tick(500) + jest.advanceTimersByTime(500) expect(result).not.toBeNull() - })) + jest.useRealTimers() + }) - it('should support date select, emit datesSet change', fakeAsync(() => { + it('should support date select, emit datesSet change', () => { + jest.useFakeTimers() let result: DateSelection component.datesSet.subscribe((date) => (result = date)) const input: HTMLInputElement = fixture.nativeElement.querySelector('input') input.value = '5/30/2023' input.dispatchEvent(new Event('dateSelect')) - tick(500) + jest.advanceTimersByTime(500) expect(result).not.toBeNull() - })) + jest.useRealTimers() + }) - it('should support relative dates', fakeAsync(() => { + it('should support relative dates', () => { + jest.useFakeTimers() let result: DateSelection component.datesSet.subscribe((date) => (result = date)) component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown @@ -88,7 +88,7 @@ describe('DatesDropdownComponent', () => { } as any) component.addedRelativeDate = RelativeDate.WITHIN_1_WEEK // normally set by ngModel binding in dropdown component.onSetAddedRelativeDate({ id: RelativeDate.WITHIN_1_WEEK } as any) - tick(500) + jest.advanceTimersByTime(500) expect(result).toEqual({ createdFrom: null, createdTo: null, @@ -97,7 +97,8 @@ describe('DatesDropdownComponent', () => { addedTo: null, addedRelativeDateID: RelativeDate.WITHIN_1_WEEK, }) - })) + jest.useRealTimers() + }) it('should support report if active', () => { component.createdRelativeDate = RelativeDate.WITHIN_1_WEEK @@ -177,11 +178,12 @@ describe('DatesDropdownComponent', () => { expect(eventSpy).toHaveBeenCalled() }) - it('should support debounce', fakeAsync(() => { + it('should support debounce', () => { + jest.useFakeTimers() let result: DateSelection component.datesSet.subscribe((date) => (result = date)) component.onChangeDebounce() - tick(500) + jest.advanceTimersByTime(500) expect(result).toEqual({ createdFrom: null, createdTo: null, @@ -190,5 +192,6 @@ describe('DatesDropdownComponent', () => { addedTo: null, addedRelativeDateID: null, }) - })) + jest.useRealTimers() + }) }) diff --git a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts index 467aabc4f..9e1661a13 100644 --- a/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component.spec.ts @@ -41,20 +41,20 @@ describe('CorrespondentEditDialogComponent', () => { fixture = TestBed.createComponent(CorrespondentEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts index 419fd89bb..970c7f364 100644 --- a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.spec.ts @@ -44,33 +44,33 @@ describe('CustomFieldEditDialogComponent', () => { fixture = TestBed.createComponent(CustomFieldEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) it('should disable data type select on edit', () => { - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() component.ngOnInit() expect(component.objectForm.get('data_type').disabled).toBeTruthy() }) it('should initialize select options on edit', () => { - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) component.object = { id: 1, name: 'Field 1', @@ -91,7 +91,7 @@ describe('CustomFieldEditDialogComponent', () => { }) it('should support add / remove select options', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) fixture.detectChanges() component.ngOnInit() expect( @@ -115,7 +115,7 @@ describe('CustomFieldEditDialogComponent', () => { const selectOptionInputs = component[ 'selectOptionInputs' ] as QueryList - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) component.objectForm.get('data_type').setValue(CustomFieldDataType.Select) component.ngOnInit() component.ngAfterViewInit() @@ -125,7 +125,7 @@ describe('CustomFieldEditDialogComponent', () => { }) it('should send all select options including those changed in form on save', () => { - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) component.object = { id: 1, name: 'Field 1', diff --git a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts index 8e8bddfab..78fd7e748 100644 --- a/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component.ts @@ -149,7 +149,7 @@ export class CustomFieldEditDialogComponent } get typeFieldDisabled(): boolean { - return this.dialogMode === EditDialogMode.EDIT + return this.dialogMode() === EditDialogMode.EDIT } private updateSelectOptions() { diff --git a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts index c3c46c980..c107c39d3 100644 --- a/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component.spec.ts @@ -41,20 +41,20 @@ describe('DocumentTypeEditDialogComponent', () => { fixture = TestBed.createComponent(DocumentTypeEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts index 1dbd54edf..6dd1e11c9 100644 --- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.spec.ts @@ -4,12 +4,7 @@ import { provideHttpClientTesting, } from '@angular/common/http/testing' import { Component } from '@angular/core' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormControl, FormGroup, @@ -122,7 +117,7 @@ describe('EditDialogComponent', () => { tagService = TestBed.inject(TagService) permissionsService = TestBed.inject(PermissionsService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = currentUser + settingsService.currentUser.set(currentUser as any) permissionsService.initialize([], currentUser as any) activeModal = TestBed.inject(NgbActiveModal) httpTestingController = TestBed.inject(HttpTestingController) @@ -136,7 +131,7 @@ describe('EditDialogComponent', () => { it('should interpolate object permissions', () => { component.getMatchingAlgorithms() // coverage component.object = tag - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) component.ngOnInit() expect(component.objectForm.get('permissions_form').value).toEqual({ @@ -145,15 +140,17 @@ describe('EditDialogComponent', () => { }) }) - it('should delay close enabled', fakeAsync(() => { + it('should delay close enabled', () => { + jest.useFakeTimers() expect(component.closeEnabled).toBeFalsy() component.ngOnInit() - tick(100) + jest.advanceTimersByTime(100) expect(component.closeEnabled).toBeTruthy() - })) + jest.useRealTimers() + }) it('should set default owner when in create mode if unset', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) component.ngOnInit() expect(component.objectForm.get('permissions_form').value.owner).toEqual( currentUser.id @@ -164,7 +161,7 @@ describe('EditDialogComponent', () => { }) it('should set default perms when in create mode if set', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_OWNER, 11) settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_USERS, [1, 2]) settingsService.set(SETTINGS_KEYS.DEFAULT_PERMS_VIEW_GROUPS, [3]) @@ -203,18 +200,18 @@ describe('EditDialogComponent', () => { }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') - fixture.detectChanges() + component.getTitle() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT - fixture.detectChanges() + component.dialogMode.set(EditDialogMode.EDIT) + component.getTitle() expect(editTitleSpy).toHaveBeenCalled() // coverage - component.dialogMode = null - fixture.detectChanges() + component.dialogMode.set(null) + component.getTitle() }) it('should close on cancel', () => { @@ -225,14 +222,14 @@ describe('EditDialogComponent', () => { it('should update an object on save in edit mode', () => { const updateSpy = jest.spyOn(tagService, 'update') - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) component.save() expect(updateSpy).toHaveBeenCalled() }) it('should not submit owner or permissions for non-owner edits', () => { component.object = tag - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) component.ngOnInit() component.objectForm.get('name').setValue('Updated tag') @@ -251,7 +248,7 @@ describe('EditDialogComponent', () => { it('should create an object on save in edit mode', () => { const createSpy = jest.spyOn(tagService, 'create') - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) component.save() expect(createSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts index 3d9026833..456ba69f9 100644 --- a/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/edit-dialog.component.ts @@ -5,6 +5,7 @@ import { OnInit, Output, inject, + model, } from '@angular/core' import { FormGroup } from '@angular/forms' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' @@ -45,10 +46,7 @@ export abstract class EditDialogComponent< protected settingsService = inject(SettingsService) protected permissionsService = inject(PermissionsService) - users: User[] - - @Input() - dialogMode: EditDialogMode = EditDialogMode.CREATE + dialogMode = model(EditDialogMode.CREATE) @Input() object: T @@ -59,18 +57,20 @@ export abstract class EditDialogComponent< @Output() failed = new EventEmitter() + users: User[] + networkActive = false closeEnabled = false - error = null + error: any = null abstract getForm(): FormGroup objectForm: FormGroup = this.getForm() ngOnInit(): void { - if (this.object != null && this.dialogMode !== EditDialogMode.CREATE) { + if (this.object != null && this.dialogMode() !== EditDialogMode.CREATE) { this.object['permissions_form'] = { owner: (this.object as ObjectWithPermissions).owner, set_permissions: (this.object as ObjectWithPermissions).permissions, @@ -124,7 +124,7 @@ export abstract class EditDialogComponent< } getTitle() { - switch (this.dialogMode) { + switch (this.dialogMode()) { case EditDialogMode.CREATE: return this.getCreateTitle() case EditDialogMode.EDIT: @@ -151,7 +151,7 @@ export abstract class EditDialogComponent< protected shouldSubmitPermissions(): boolean { return ( - this.dialogMode === EditDialogMode.CREATE || + this.dialogMode() === EditDialogMode.CREATE || this.permissionsService.currentUserOwnsObject(this.object) ) } @@ -172,7 +172,7 @@ export abstract class EditDialogComponent< delete newObject['set_permissions'] } var serverResponse: Observable - switch (this.dialogMode) { + switch (this.dialogMode()) { case EditDialogMode.CREATE: serverResponse = this.service.create(newObject) break diff --git a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts index 88e989942..b7002853b 100644 --- a/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/group-edit-dialog/group-edit-dialog.component.spec.ts @@ -45,20 +45,20 @@ describe('GroupEditDialogComponent', () => { fixture = TestBed.createComponent(GroupEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html index feb834035..af99a5f91 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/mail-account-edit-dialog/mail-account-edit-dialog.component.html @@ -25,8 +25,8 @@
        - @if (testResult) { - {{testResultMessage}} + @if (testResult()) { + {{testResultMessage}} }
        - +
        @@ -65,10 +65,10 @@
        - + @if (showCorrespondentField) { - + }
        diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts index af3b4dbd8..a818c752b 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.spec.ts @@ -75,20 +75,20 @@ describe('MailRuleEditDialogComponent', () => { fixture = TestBed.createComponent(MailRuleEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts index 436569a08..a123c097c 100644 --- a/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/mail-rule-edit-dialog/mail-rule-edit-dialog.component.ts @@ -1,11 +1,12 @@ import { Component, inject } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, } from '@angular/forms' -import { first } from 'rxjs' +import { map } from 'rxjs' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { Correspondent } from 'src/app/data/correspondent' import { DocumentType } from 'src/app/data/document-type' @@ -154,37 +155,28 @@ const METADATA_CORRESPONDENT_OPTIONS = [ ], }) export class MailRuleEditDialogComponent extends EditDialogComponent { - private accountService: MailAccountService - private correspondentService: CorrespondentService - private documentTypeService: DocumentTypeService + private readonly accountService = inject(MailAccountService) + private readonly correspondentService = inject(CorrespondentService) + private readonly documentTypeService = inject(DocumentTypeService) - accounts: MailAccount[] - correspondents: Correspondent[] - documentTypes: DocumentType[] + readonly accounts = toSignal( + this.accountService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as MailAccount[] } + ) + readonly correspondents = toSignal( + this.correspondentService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as Correspondent[] } + ) + readonly documentTypes = toSignal( + this.documentTypeService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as DocumentType[] } + ) constructor() { super() this.service = inject(MailRuleService) - this.accountService = inject(MailAccountService) - this.correspondentService = inject(CorrespondentService) - this.documentTypeService = inject(DocumentTypeService) this.userService = inject(UserService) this.settingsService = inject(SettingsService) - - this.accountService - .listAll() - .pipe(first()) - .subscribe((result) => (this.accounts = result.results)) - - this.correspondentService - .listAll() - .pipe(first()) - .subscribe((result) => (this.correspondents = result.results)) - - this.documentTypeService - .listAll() - .pipe(first()) - .subscribe((result) => (this.documentTypes = result.results)) } getCreateTitle() { diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts index 2466ced73..67bbba641 100644 --- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.spec.ts @@ -30,20 +30,20 @@ describe('StoragePathEditDialogComponent', () => { documentService = TestBed.inject(DocumentService) fixture = TestBed.createComponent(StoragePathEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts index 68ce40f5e..f0d17a965 100644 --- a/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component.ts @@ -67,11 +67,11 @@ export class StoragePathEditDialogComponent private testDocument: Document public testResult: string public testFailed: boolean = false - public loading = false public testLoading = false constructor() { super() + this.loading.set(false) this.service = inject(StoragePathService) this.userService = inject(UserService) this.settingsService = inject(SettingsService) @@ -138,7 +138,7 @@ export class StoragePathEditDialogComponent this.documentsInput$.pipe( distinctUntilChanged(), takeUntil(this.unsubscribeNotifier), - tap(() => (this.loading = true)), + tap(() => this.loading.set(true)), switchMap((title) => this.documentsService .listFiltered( @@ -152,7 +152,7 @@ export class StoragePathEditDialogComponent .pipe( map((result) => result.results), catchError(() => of([])), // empty on error - tap(() => (this.loading = false)) + tap(() => this.loading.set(false)) ) ) ) diff --git a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts index 16fb7a9f3..c16130a18 100644 --- a/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component.spec.ts @@ -48,20 +48,20 @@ describe('TagEditDialogComponent', () => { fixture = TestBed.createComponent(TagEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html index df1593073..3bab247dc 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html @@ -31,7 +31,7 @@ - + @if (object?.is_mfa_enabled && currentUserIsSuperUser) { @@ -42,7 +42,7 @@ i18n-title buttonClasses="btn-outline-danger btn-sm" iconName="trash" - [disabled]="totpLoading" + [disabled]="totpLoading()" (confirm)="deactivateTotp()"> } diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts index 9ffa1ea95..f50bf830e 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts @@ -73,7 +73,7 @@ describe('UserEditDialogComponent', () => { fixture = TestBed.createComponent(UserEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) permissionsService = TestBed.inject(PermissionsService) toastService = TestBed.inject(ToastService) component = fixture.componentInstance @@ -82,13 +82,13 @@ describe('UserEditDialogComponent', () => { }) it('should support create and edit modes', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() expect(createTitleSpy).toHaveBeenCalled() expect(editTitleSpy).not.toHaveBeenCalled() - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) @@ -114,17 +114,17 @@ describe('UserEditDialogComponent', () => { it('should detect whether password was changed in form on save', () => { component.objectForm.get('password').setValue(null) component.save() - expect(component.passwordIsSet).toBeFalsy() + expect(component.passwordIsSet()).toBeFalsy() // unchanged pw component.objectForm.get('password').setValue('*******') component.save() - expect(component.passwordIsSet).toBeFalsy() + expect(component.passwordIsSet()).toBeFalsy() // unchanged pw component.objectForm.get('password').setValue('helloworld') component.save() - expect(component.passwordIsSet).toBeTruthy() + expect(component.passwordIsSet()).toBeTruthy() }) it('should support deactivation of TOTP', () => { diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts index 215b2c137..426fc67e2 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts @@ -1,11 +1,12 @@ -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, inject, signal } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { FormControl, FormGroup, FormsModule, ReactiveFormsModule, } from '@angular/forms' -import { first } from 'rxjs' +import { first, map } from 'rxjs' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { Group } from 'src/app/data/group' import { User } from 'src/app/data/user' @@ -38,22 +39,19 @@ export class UserEditDialogComponent implements OnInit { private toastService = inject(ToastService) - private groupsService: GroupService + private readonly groupsService = inject(GroupService) - groups: Group[] - passwordIsSet: boolean = false - public totpLoading: boolean = false + readonly groups = toSignal( + this.groupsService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as Group[] } + ) + readonly passwordIsSet = signal(false) + readonly totpLoading = signal(false) constructor() { super() this.service = inject(UserService) - this.groupsService = inject(GroupService) this.settingsService = inject(SettingsService) - - this.groupsService - .listAll() - .pipe(first()) - .subscribe((result) => (this.groups = result.results)) } ngOnInit(): void { @@ -103,14 +101,15 @@ export class UserEditDialogComponent if (!groupsVal) return [] else return groupsVal.flatMap( - (id) => this.groups.find((g) => g.id == id)?.permissions + (id) => this.groups()?.find((g) => g.id == id)?.permissions ) } save(): void { - this.passwordIsSet = + this.passwordIsSet.set( this.objectForm.get('password').value?.toString().replaceAll('*', '') .length > 0 + ) super.save() } @@ -119,13 +118,13 @@ export class UserEditDialogComponent } deactivateTotp() { - this.totpLoading = true + this.totpLoading.set(true) ;(this.service as UserService) .deactivateTotp(this.object) .pipe(first()) .subscribe({ next: (result) => { - this.totpLoading = false + this.totpLoading.set(false) if (result) { this.toastService.showInfo($localize`Totp deactivated`) this.object.is_mfa_enabled = false @@ -134,7 +133,7 @@ export class UserEditDialogComponent } }, error: (e) => { - this.totpLoading = false + this.totpLoading.set(false) this.toastService.showError($localize`Totp deactivation failed`, e) }, }) diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html index 51b8a2a5d..9dce30891 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -140,7 +140,7 @@ @if (formGroup.get('schedule_date_field').value === 'custom_field') {
        - +
        } @@ -162,7 +162,7 @@ @if (formGroup.get('type').value === WorkflowTriggerType.Consumption) { - + } @if (formGroup.get('type').value === WorkflowTriggerType.DocumentAdded || formGroup.get('type').value === WorkflowTriggerType.DocumentUpdated || formGroup.get('type').value === WorkflowTriggerType.Scheduled) { @@ -262,10 +262,10 @@
        - - - - + + + +
        @@ -326,25 +326,25 @@
        Remove correspondents
        - +
        Remove document types
        - +
        Remove storage paths
        - +
        Remove custom fields
        - +
        diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts index 070e5124f..abc773ddf 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.spec.ts @@ -187,14 +187,14 @@ describe('WorkflowEditDialogComponent', () => { fixture = TestBed.createComponent(WorkflowEditDialogComponent) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 99, username: 'user99' } + settingsService.currentUser.set({ id: 99, username: 'user99' }) component = fixture.componentInstance fixture.detectChanges() }) it('should support create and edit modes, support adding triggers and actions on new workflow', () => { - component.dialogMode = EditDialogMode.CREATE + component.dialogMode.set(EditDialogMode.CREATE) const createTitleSpy = jest.spyOn(component, 'getCreateTitle') const editTitleSpy = jest.spyOn(component, 'getEditTitle') fixture.detectChanges() @@ -209,7 +209,7 @@ describe('WorkflowEditDialogComponent', () => { expect(component.object).not.toBeUndefined() expect(component.object.triggers).toHaveLength(1) - component.dialogMode = EditDialogMode.EDIT + component.dialogMode.set(EditDialogMode.EDIT) fixture.detectChanges() expect(editTitleSpy).toHaveBeenCalled() }) @@ -771,25 +771,21 @@ describe('WorkflowEditDialogComponent', () => { false ) - component.correspondents = [{ id: 1, name: 'C1' } as any] - component.documentTypes = [{ id: 2, name: 'DT' } as any] - component.storagePaths = [{ id: 3, name: 'SP' } as any] - expect( component.getFilterSelectItems(TriggerFilterType.CorrespondentIs) - ).toEqual(component.correspondents) + ).toEqual(component.correspondents()) expect( component.getFilterSelectItems(TriggerFilterType.DocumentTypeIs) - ).toEqual(component.documentTypes) + ).toEqual(component.documentTypes()) expect( component.getFilterSelectItems(TriggerFilterType.DocumentTypeAny) - ).toEqual(component.documentTypes) + ).toEqual(component.documentTypes()) expect( component.getFilterSelectItems(TriggerFilterType.StoragePathIs) - ).toEqual(component.storagePaths) + ).toEqual(component.storagePaths()) expect( component.getFilterSelectItems(TriggerFilterType.StoragePathAny) - ).toEqual(component.storagePaths) + ).toEqual(component.storagePaths()) expect(component.getFilterSelectItems(TriggerFilterType.TagsAll)).toEqual( [] ) diff --git a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts index 83e7a40f9..4db656149 100644 --- a/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.ts @@ -4,7 +4,8 @@ import { moveItemInArray, } from '@angular/cdk/drag-drop' import { NgTemplateOutlet } from '@angular/common' -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, computed, inject, signal } from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { AbstractControl, FormArray, @@ -15,7 +16,7 @@ import { } from '@angular/forms' import { NgbAccordionModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Subscription, first, takeUntil } from 'rxjs' +import { Subscription, map, takeUntil } from 'rxjs' import { Correspondent } from 'src/app/data/correspondent' import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field' import { DocumentType } from 'src/app/data/document-type' @@ -470,23 +471,40 @@ export class WorkflowEditDialogComponent public TriggerFilterType = TriggerFilterType public filterDefinitions = TRIGGER_FILTER_DEFINITIONS - private correspondentService: CorrespondentService - private documentTypeService: DocumentTypeService - private storagePathService: StoragePathService - private mailRuleService: MailRuleService - private customFieldsService: CustomFieldsService + private readonly correspondentService = inject(CorrespondentService) + private readonly documentTypeService = inject(DocumentTypeService) + private readonly storagePathService = inject(StoragePathService) + private readonly mailRuleService = inject(MailRuleService) + private readonly customFieldsService = inject(CustomFieldsService) - templates: Workflow[] - correspondents: Correspondent[] - documentTypes: DocumentType[] - storagePaths: StoragePath[] - mailRules: MailRule[] - customFields: CustomField[] - dateCustomFields: CustomField[] + readonly templates = signal(undefined) + readonly correspondents = toSignal( + this.correspondentService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as Correspondent[] } + ) + readonly documentTypes = toSignal( + this.documentTypeService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as DocumentType[] } + ) + readonly storagePaths = toSignal( + this.storagePathService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as StoragePath[] } + ) + readonly mailRules = toSignal( + this.mailRuleService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as MailRule[] } + ) + readonly customFields = toSignal( + this.customFieldsService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as CustomField[] } + ) + readonly dateCustomFields = computed(() => + this.customFields()?.filter((f) => f.data_type === CustomFieldDataType.Date) + ) expandedItem: number = null - private allowedActionTypes = [] + readonly allowedActionTypes = signal([]) private readonly triggerFilterOptionsMap = new WeakMap< FormArray, @@ -496,43 +514,8 @@ export class WorkflowEditDialogComponent constructor() { super() this.service = inject(WorkflowService) - this.correspondentService = inject(CorrespondentService) - this.documentTypeService = inject(DocumentTypeService) - this.storagePathService = inject(StoragePathService) - this.mailRuleService = inject(MailRuleService) this.userService = inject(UserService) this.settingsService = inject(SettingsService) - this.customFieldsService = inject(CustomFieldsService) - - this.correspondentService - .listAll() - .pipe(first()) - .subscribe((result) => (this.correspondents = result.results)) - - this.documentTypeService - .listAll() - .pipe(first()) - .subscribe((result) => (this.documentTypes = result.results)) - - this.storagePathService - .listAll() - .pipe(first()) - .subscribe((result) => (this.storagePaths = result.results)) - - this.mailRuleService - .listAll() - .pipe(first()) - .subscribe((result) => (this.mailRules = result.results)) - - this.customFieldsService - .listAll() - .pipe(first()) - .subscribe((result) => { - this.customFields = result.results - this.dateCustomFields = this.customFields?.filter( - (f) => f.data_type === CustomFieldDataType.Date - ) - }) } getCreateTitle() { @@ -565,11 +548,13 @@ export class WorkflowEditDialogComponent this.checkRemovalActionFields.bind(this) ) this.checkRemovalActionFields(this.objectForm.value) - this.allowedActionTypes = this.settingsService.get( - SETTINGS_KEYS.EMAIL_ENABLED + this.allowedActionTypes.set( + this.settingsService.get(SETTINGS_KEYS.EMAIL_ENABLED) + ? WORKFLOW_ACTION_OPTIONS + : WORKFLOW_ACTION_OPTIONS.filter( + (a) => a.id !== WorkflowActionType.Email + ) ) - ? WORKFLOW_ACTION_OPTIONS - : WORKFLOW_ACTION_OPTIONS.filter((a) => a.id !== WorkflowActionType.Email) } private checkRemovalActionFields(formWorkflow: Workflow) { @@ -954,11 +939,11 @@ export class WorkflowEditDialogComponent switch (definition.selectItems) { case 'correspondents': - return this.correspondents + return this.correspondents() case 'documentTypes': - return this.documentTypes + return this.documentTypes() case 'storagePaths': - return this.storagePaths + return this.storagePaths() default: return [] } @@ -1294,7 +1279,8 @@ export class WorkflowEditDialogComponent } get actionTypeOptions() { - return this.allowedActionTypes + this.settingsService.trackChanges() + return this.allowedActionTypes() } getActionTypeOptionName(type: WorkflowActionType): string { diff --git a/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html b/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html index 079790c4b..3a5d5799a 100644 --- a/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html +++ b/src-ui/src/app/components/common/email-document-dialog/email-document-dialog.component.html @@ -1,8 +1,8 @@

        { - documentIds.length, + documentIds().length, plural, - =1 {Email Document} other {Email {{documentIds.length}} Documents} + =1 {Email Document} other {Email {{documentIds().length}} Documents} }

        @@ -23,11 +23,11 @@
        - +
        -
        - @for (key of hotkeys.entries(); track key[0]) { + @for (key of hotkeys().entries(); track key[0]) { + @@ -37,7 +37,7 @@ - @for (mail of processedMails; track mail.id) { + @for (mail of processedMails(); track mail.id) {
        {{mail.status}} @@ -46,7 +46,7 @@
        @@ -82,7 +82,7 @@
        {{ key[1] }} diff --git a/src-ui/src/app/components/common/hotkey-dialog/hotkey-dialog.component.ts b/src-ui/src/app/components/common/hotkey-dialog/hotkey-dialog.component.ts index 180fc1005..acf9f2646 100644 --- a/src-ui/src/app/components/common/hotkey-dialog/hotkey-dialog.component.ts +++ b/src-ui/src/app/components/common/hotkey-dialog/hotkey-dialog.component.ts @@ -1,4 +1,4 @@ -import { Component, inject } from '@angular/core' +import { Component, inject, signal } from '@angular/core' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' const SYMBOLS = { @@ -20,9 +20,8 @@ const SYMBOLS = { }) export class HotkeyDialogComponent { activeModal = inject(NgbActiveModal) - - public title: string = $localize`Keyboard shortcuts` - public hotkeys: Map = new Map() + readonly title = signal($localize`Keyboard shortcuts`) + readonly hotkeys = signal>(new Map()) public close(): void { this.activeModal.close() diff --git a/src-ui/src/app/components/common/input/abstract-input.ts b/src-ui/src/app/components/common/input/abstract-input.ts index 5e95de0bf..252da855f 100644 --- a/src-ui/src/app/components/common/input/abstract-input.ts +++ b/src-ui/src/app/components/common/input/abstract-input.ts @@ -1,4 +1,5 @@ import { + ChangeDetectorRef, Directive, ElementRef, EventEmitter, @@ -6,12 +7,15 @@ import { OnInit, Output, ViewChild, + inject, } from '@angular/core' import { ControlValueAccessor } from '@angular/forms' import { v4 as uuidv4 } from 'uuid' @Directive() export class AbstractInputComponent implements OnInit, ControlValueAccessor { + protected readonly changeDetector = inject(ChangeDetectorRef) + @ViewChild('inputField') inputField: ElementRef @@ -23,6 +27,7 @@ export class AbstractInputComponent implements OnInit, ControlValueAccessor { writeValue(newValue: any): void { this.value = newValue + this.changeDetector.markForCheck() } registerOnChange(fn: any): void { this.onChange = fn @@ -32,6 +37,7 @@ export class AbstractInputComponent implements OnInit, ControlValueAccessor { } setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled + this.changeDetector.markForCheck() } focus() { diff --git a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.html b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.html index 6cf5689a9..b3377da2d 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.html +++ b/src-ui/src/app/components/common/input/permissions/permissions-group/permissions-group.component.html @@ -3,7 +3,7 @@ { - groups: Group[] - - constructor() { - const groupService = inject(GroupService) - - super() - groupService - .listAll() - .pipe(first()) - .subscribe((result) => (this.groups = result.results)) - } + private readonly groupService = inject(GroupService) + readonly groups = toSignal( + this.groupService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as Group[] } + ) } diff --git a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.html b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.html index 9e0e2025d..18a56d89c 100644 --- a/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.html +++ b/src-ui/src/app/components/common/input/permissions/permissions-user/permissions-user.component.html @@ -3,7 +3,7 @@ { - users: User[] - - constructor() { - const userService = inject(UserService) - - super() - userService - .listAll() - .pipe(first()) - .subscribe((result) => (this.users = result.results)) - } + private readonly userService = inject(UserService) + readonly users = toSignal( + this.userService.listAll().pipe(map((result) => result.results)), + { initialValue: undefined as User[] } + ) } diff --git a/src-ui/src/app/components/common/input/select/select.component.spec.ts b/src-ui/src/app/components/common/input/select/select.component.spec.ts index 9104faf45..a71f37afb 100644 --- a/src-ui/src/app/components/common/input/select/select.component.spec.ts +++ b/src-ui/src/app/components/common/input/select/select.component.spec.ts @@ -1,9 +1,4 @@ -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, NG_VALUE_ACCESSOR, @@ -121,12 +116,14 @@ describe('SelectComponent', () => { ).toBeFalsy() }) - it('should clear search term on blur after delay', fakeAsync(() => { + it('should clear search term on blur after delay', () => { + jest.useFakeTimers() const clearSpy = jest.spyOn(component, 'clearLastSearchTerm') component.onBlur() - tick(3000) + jest.advanceTimersByTime(3000) expect(clearSpy).toHaveBeenCalled() - })) + jest.useRealTimers() + }) it('should emit filtered documents', () => { component.value = 10 diff --git a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts index 1b761a5db..054a79394 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.spec.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.spec.ts @@ -106,7 +106,7 @@ describe('TagsComponent', () => { modalService = TestBed.inject(NgbModal) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 } as any) fixture = TestBed.createComponent(TagsComponent) fixture.debugElement.injector.get(NG_VALUE_ACCESSOR) component = fixture.componentInstance @@ -136,20 +136,13 @@ describe('TagsComponent', () => { }) it('should support create new using last search term and open a modal', () => { - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) let activeInstances: NgbModalRef[] modalService.activeInstances.subscribe((v) => (activeInstances = v)) component.select.filter('foobar') component.createTag() expect(modalService.hasOpenModals()).toBeTruthy() expect(activeInstances[0].componentInstance.object.name).toEqual('foobar') - const editDialog = activeInstances[0] - .componentInstance as TagEditDialogComponent - editDialog.save() // create is mocked - fixture.detectChanges() - fixture.whenStable().then(() => { - expect(fixture.debugElement.nativeElement.textContent).toContain('foobar') - }) }) it('support remove tags', () => { diff --git a/src-ui/src/app/components/common/input/tags/tags.component.ts b/src-ui/src/app/components/common/input/tags/tags.component.ts index 6b7d3150f..e1447e214 100644 --- a/src-ui/src/app/components/common/input/tags/tags.component.ts +++ b/src-ui/src/app/components/common/input/tags/tags.component.ts @@ -1,4 +1,5 @@ import { + ChangeDetectorRef, Component, EventEmitter, forwardRef, @@ -49,6 +50,7 @@ import { TagComponent } from '../../tag/tag.component' export class TagsComponent implements OnInit, ControlValueAccessor { private tagService = inject(TagService) private modalService = inject(NgbModal) + private readonly changeDetector = inject(ChangeDetectorRef) constructor() { this.createTagRef = this.createTag.bind(this) @@ -60,6 +62,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor { writeValue(newValue: number[]): void { this.value = newValue + this.changeDetector.markForCheck() } registerOnChange(fn: any): void { this.onChange = fn @@ -69,11 +72,13 @@ export class TagsComponent implements OnInit, ControlValueAccessor { } setDisabledState?(isDisabled: boolean): void { this.disabled = isDisabled + this.changeDetector.markForCheck() } ngOnInit(): void { this.tagService.listAll().subscribe((result) => { this.tags = result.results + this.changeDetector.markForCheck() }) } @@ -176,7 +181,7 @@ export class TagsComponent implements OnInit, ControlValueAccessor { var modal = this.modalService.open(TagEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) if (name) modal.componentInstance.object = { name: name } else if (this.select.searchTerm) modal.componentInstance.object = { name: this.select.searchTerm } diff --git a/src-ui/src/app/components/common/logo/logo.component.html b/src-ui/src/app/components/common/logo/logo.component.html index 15a7ee71c..f8ca689d7 100644 --- a/src-ui/src/app/components/common/logo/logo.component.html +++ b/src-ui/src/app/components/common/logo/logo.component.html @@ -1,7 +1,7 @@ @if (customLogo) { - + } @else { - + diff --git a/src-ui/src/app/components/common/logo/logo.component.spec.ts b/src-ui/src/app/components/common/logo/logo.component.spec.ts index cb6112016..d210167c7 100644 --- a/src-ui/src/app/components/common/logo/logo.component.spec.ts +++ b/src-ui/src/app/components/common/logo/logo.component.spec.ts @@ -28,7 +28,7 @@ describe('LogoComponent', () => { it('should support extra classes', () => { expect(fixture.debugElement.queryAll(By.css('.foo'))).toHaveLength(0) - component.extra_classes = 'foo' + fixture.componentRef.setInput('extra_classes', 'foo') fixture.detectChanges() expect(fixture.debugElement.queryAll(By.css('.foo'))).toHaveLength(1) }) @@ -37,7 +37,7 @@ describe('LogoComponent', () => { expect(fixture.debugElement.query(By.css('svg')).attributes.style).toEqual( 'height:6em' ) - component.height = '10em' + fixture.componentRef.setInput('height', '10em') fixture.detectChanges() expect(fixture.debugElement.query(By.css('svg')).attributes.style).toEqual( 'height:10em' diff --git a/src-ui/src/app/components/common/logo/logo.component.ts b/src-ui/src/app/components/common/logo/logo.component.ts index e15be24bf..f9b0a09c3 100644 --- a/src-ui/src/app/components/common/logo/logo.component.ts +++ b/src-ui/src/app/components/common/logo/logo.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, inject } from '@angular/core' +import { Component, inject, input } from '@angular/core' import { SETTINGS_KEYS } from 'src/app/data/ui-settings' import { SettingsService } from 'src/app/services/settings.service' import { environment } from 'src/environments/environment' @@ -10,12 +10,8 @@ import { environment } from 'src/environments/environment' }) export class LogoComponent { private settingsService = inject(SettingsService) - - @Input() - extra_classes: string - - @Input() - height = '6em' + readonly extra_classes = input(undefined) + readonly height = input('6em') get customLogo(): string { return this.settingsService.get(SETTINGS_KEYS.APP_LOGO)?.length @@ -27,6 +23,6 @@ export class LogoComponent { } getClasses() { - return ['logo'].concat(this.extra_classes).join(' ') + return ['logo'].concat(this.extra_classes()).join(' ') } } diff --git a/src-ui/src/app/components/common/page-header/page-header.component.html b/src-ui/src/app/components/common/page-header/page-header.component.html index 6ee0388cd..05df7dcd5 100644 --- a/src-ui/src/app/components/common/page-header/page-header.component.html +++ b/src-ui/src/app/components/common/page-header/page-header.component.html @@ -1,32 +1,32 @@

        - {{title}} - @if (id) { + {{title()}} + @if (id()) { - @if (copied) { + @if (copied()) { Copied! } @else { - ID: {{id}} + ID: {{id()}} } } - @if (subTitle) { - {{subTitle}} + @if (subTitle()) { + {{subTitle()}} } - @if (info) { + @if (info()) { -

        - @if (infoLink) { - Read more +

        + @if (infoLink()) { + Read more }
        } - @if (loading) { + @if (loading()) { Loading... }

        diff --git a/src-ui/src/app/components/common/page-header/page-header.component.spec.ts b/src-ui/src/app/components/common/page-header/page-header.component.spec.ts index 8fa9ad369..1bdcd4279 100644 --- a/src-ui/src/app/components/common/page-header/page-header.component.spec.ts +++ b/src-ui/src/app/components/common/page-header/page-header.component.spec.ts @@ -1,6 +1,7 @@ import { Clipboard } from '@angular/cdk/clipboard' import { ComponentFixture, TestBed } from '@angular/core/testing' import { Title } from '@angular/platform-browser' +import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { environment } from 'src/environments/environment' import { PageHeaderComponent } from './page-header.component' @@ -13,7 +14,7 @@ describe('PageHeaderComponent', () => { beforeEach(async () => { TestBed.configureTestingModule({ providers: [], - imports: [PageHeaderComponent], + imports: [NgxBootstrapIconsModule.pick(allIcons), PageHeaderComponent], }).compileComponents() titleService = TestBed.inject(Title) @@ -23,9 +24,13 @@ describe('PageHeaderComponent', () => { fixture.detectChanges() }) + afterEach(() => { + jest.useRealTimers() + }) + it('should display title + subtitle', () => { - component.title = 'Foo' - component.subTitle = 'Bar' + fixture.componentRef.setInput('title', 'Foo') + fixture.componentRef.setInput('subTitle', 'Bar') fixture.detectChanges() expect(fixture.nativeElement.textContent).toContain('Foo') expect(fixture.nativeElement.textContent).toContain('Bar') @@ -33,19 +38,20 @@ describe('PageHeaderComponent', () => { it('should set html title', () => { const titleSpy = jest.spyOn(titleService, 'setTitle') - component.title = 'Foo Bar' + fixture.componentRef.setInput('title', 'Foo Bar') + fixture.detectChanges() expect(titleSpy).toHaveBeenCalledWith(`Foo Bar - ${environment.appTitle}`) }) it('should copy id to clipboard, reset after 3 seconds', () => { jest.useFakeTimers() - component.id = 42 as any + fixture.componentRef.setInput('id', 42) jest.spyOn(clipboard, 'copy').mockReturnValue(true) component.copyID() expect(clipboard.copy).toHaveBeenCalledWith('42') - expect(component.copied).toBe(true) + expect(component.copied()).toBe(true) jest.advanceTimersByTime(3000) - expect(component.copied).toBe(false) + expect(component.copied()).toBe(false) }) }) diff --git a/src-ui/src/app/components/common/page-header/page-header.component.ts b/src-ui/src/app/components/common/page-header/page-header.component.ts index ec0525b4b..ec209bf2e 100644 --- a/src-ui/src/app/components/common/page-header/page-header.component.ts +++ b/src-ui/src/app/components/common/page-header/page-header.component.ts @@ -1,5 +1,5 @@ import { Clipboard } from '@angular/cdk/clipboard' -import { Component, Input, inject } from '@angular/core' +import { Component, effect, inject, input, signal } from '@angular/core' import { Title } from '@angular/platform-browser' import { NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @@ -16,40 +16,26 @@ export class PageHeaderComponent { private titleService = inject(Title) private clipboard = inject(Clipboard) - private _title = '' - public copied: boolean = false + readonly id = input(undefined) + readonly subTitle = input('') + readonly info = input(undefined) + readonly infoLink = input(undefined) + readonly loading = input(false) + readonly title = input('') + readonly copied = signal(false) private copyTimeout: any - @Input() - set title(title: string) { - this._title = title - this.titleService.setTitle(`${this.title} - ${environment.appTitle}`) + constructor() { + effect(() => { + this.titleService.setTitle(`${this.title()} - ${environment.appTitle}`) + }) } - get title() { - return this._title - } - - @Input() - id: number - - @Input() - subTitle: string = '' - - @Input() - info: string - - @Input() - infoLink: string - - @Input() - loading: boolean = false - public copyID() { - this.copied = this.clipboard.copy(this.id.toString()) + this.copied.set(this.clipboard.copy(this.id().toString())) clearTimeout(this.copyTimeout) this.copyTimeout = setTimeout(() => { - this.copied = false + this.copied.set(false) }, 3000) } } diff --git a/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.spec.ts b/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.spec.ts index 0e9abafbb..41fee5a7e 100644 --- a/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.spec.ts +++ b/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.spec.ts @@ -146,8 +146,8 @@ describe('PDFEditorComponent', () => { const previewSpy = jest .spyOn(documentService, 'getPreviewUrl') .mockReturnValue('preview-version') - component.documentID = 3 - component.versionID = 10 + component.documentID.set(3) + component.versionID.set(10) expect(component.pdfSrc).toBe('preview-version') expect(previewSpy).toHaveBeenCalledWith(3, false, 10) diff --git a/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.ts b/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.ts index 5ea624aef..8ba458062 100644 --- a/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.ts +++ b/src-ui/src/app/components/common/pdf-editor/pdf-editor.component.ts @@ -3,7 +3,7 @@ import { DragDropModule, moveItemInArray, } from '@angular/cdk/drag-drop' -import { Component, inject } from '@angular/core' +import { Component, inject, signal } from '@angular/core' import { FormsModule } from '@angular/forms' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @@ -45,8 +45,9 @@ export class PDFEditorComponent extends ConfirmDialogComponent { private readonly settingsService = inject(SettingsService) activeModal: NgbActiveModal = inject(NgbActiveModal) - documentID: number - versionID?: number + readonly documentID = signal(undefined) + readonly versionID = signal(undefined) + pages: PageOperation[] = [] totalPages = 0 editMode: PdfEditorEditMode = this.settingsService.get( @@ -57,9 +58,9 @@ export class PDFEditorComponent extends ConfirmDialogComponent { get pdfSrc(): string { return this.documentService.getPreviewUrl( - this.documentID, + this.documentID(), false, - this.versionID + this.versionID() ) } diff --git a/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.component.ts b/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.component.ts index 5b3ea514e..2bb5273ac 100644 --- a/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.component.ts +++ b/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.component.ts @@ -27,7 +27,6 @@ import { } from 'pdfjs-dist/web/pdf_viewer.mjs' import { PdfRenderMode, - PdfSource, PdfZoomLevel, PdfZoomScale, PngxPdfDocumentProxy, @@ -43,7 +42,8 @@ export class PngxPdfViewerComponent { private readonly document = inject(DOCUMENT) - @Input() src!: PdfSource + @Input() src!: string + @Input() password?: string @Input() page?: number @Output() pageChange = new EventEmitter() @Input() rotation?: number @@ -93,7 +93,7 @@ export class PngxPdfViewerComponent } ngOnChanges(changes: SimpleChanges): void { - if (changes['src']) { + if (changes['src'] || changes['password']) { this.resetViewerState() if (this.src) { this.loadDocument() @@ -174,8 +174,12 @@ export class PngxPdfViewerComponent 'assets/js/pdf.worker.min.mjs', this.document.baseURI ).toString() - this.loadingTask = getDocument(this.src) - + let initOptions = { + url: this.src, + password: this.password, + withCredentials: true, + } + this.loadingTask = getDocument(initOptions) try { const pdf = await this.loadingTask.promise this.pdf = pdf @@ -287,7 +291,7 @@ export class PngxPdfViewerComponent this.eventBus.dispatch('find', { query, caseSensitive: false, - highlightAll: query.length > 0, + highlightAll: query?.length > 0, phraseSearch: true, }) } diff --git a/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.types.ts b/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.types.ts index edce9a7e7..42f413848 100644 --- a/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.types.ts +++ b/src-ui/src/app/components/common/pdf-viewer/pdf-viewer.types.ts @@ -2,8 +2,6 @@ export type PngxPdfDocumentProxy = { numPages: number } -export type PdfSource = string | { url: string; password?: string } - export enum PdfRenderMode { Single = 'single', All = 'all', diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html index f11fc9760..7ac84c6cc 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.html @@ -1,5 +1,5 @@
        -

        {{title}}

        +

        {{title()}}

        @@ -7,7 +7,7 @@
        - +
        @@ -16,18 +16,18 @@
        - @if (note) { + @if (note()) {
        - {{ note }} + {{ note() }}
        }
        - @if (!buttonsEnabled) { + @if (!buttonsEnabled()) {
        Loading... } - - + +
        diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts index e59671921..004b2df0b 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.spec.ts @@ -109,7 +109,7 @@ describe('PermissionsDialogComponent', () => { permissions: set_permissions.set_permissions, } component.object = obj - expect(component.title).toEqual(`Edit permissions for ${obj.name}`) + expect(component.title()).toEqual(`Edit permissions for ${obj.name}`) expect(component.permissions).toEqual(set_permissions) }) diff --git a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts index 56c26f785..1194886fd 100644 --- a/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts +++ b/src-ui/src/app/components/common/permissions-dialog/permissions-dialog.component.ts @@ -1,4 +1,12 @@ -import { Component, EventEmitter, Input, Output, inject } from '@angular/core' +import { + Component, + EventEmitter, + Input, + Output, + inject, + signal, +} from '@angular/core' +import { toSignal } from '@angular/core/rxjs-interop' import { FormControl, FormGroup, @@ -6,6 +14,7 @@ import { ReactiveFormsModule, } from '@angular/forms' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' +import { map } from 'rxjs' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' import { User } from 'src/app/data/user' import { UserService } from 'src/app/services/rest/user.service' @@ -27,26 +36,22 @@ export class PermissionsDialogComponent { activeModal = inject(NgbActiveModal) private userService = inject(UserService) - users: User[] + readonly users = toSignal( + this.userService.listAll().pipe(map((r) => r.results)), + { initialValue: undefined as User[] } + ) + readonly title = signal($localize`Set permissions`) + readonly note = signal(null) + readonly buttonsEnabled = signal(true) private o: ObjectWithPermissions = undefined - constructor() { - this.userService.listAll().subscribe((r) => (this.users = r.results)) - } - @Output() public confirmClicked = new EventEmitter() - @Input() - title = $localize`Set permissions` - - @Input() - note: string = null - @Input() set object(o: ObjectWithPermissions) { this.o = o - this.title = $localize`Edit permissions for ` + o['name'] + this.title.set($localize`Edit permissions for ` + o['name']) this.form.patchValue({ merge: true, permissions_form: { @@ -65,8 +70,6 @@ export class PermissionsDialogComponent { merge: new FormControl(true), }) - buttonsEnabled: boolean = true - get permissions() { return { owner: this.form.get('permissions_form').value?.owner ?? null, diff --git a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.spec.ts b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.spec.ts index fd8e261a0..569aa39c0 100644 --- a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.spec.ts @@ -64,9 +64,9 @@ describe('PermissionsFilterDropdownComponent', () => { { provide: SettingsService, useValue: { - currentUser: { + currentUser: () => ({ id: currentUserID, - }, + }), }, }, provideHttpClient(withInterceptorsFromDi()), diff --git a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts index 83e632b68..8476ed0f3 100644 --- a/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts +++ b/src-ui/src/app/components/common/permissions-filter-dropdown/permissions-filter-dropdown.component.ts @@ -117,12 +117,12 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions if (this.selectionModel.ownerFilter === OwnerFilterType.SELF) { this.selectionModel.includeUsers = [] this.selectionModel.excludeUsers = [] - this.selectionModel.userID = this.settingsService.currentUser.id + this.selectionModel.userID = this.settingsService.currentUser().id this.selectionModel.hideUnowned = false } else if (this.selectionModel.ownerFilter === OwnerFilterType.NOT_SELF) { this.selectionModel.userID = null this.selectionModel.includeUsers = [] - this.selectionModel.excludeUsers = [this.settingsService.currentUser.id] + this.selectionModel.excludeUsers = [this.settingsService.currentUser().id] this.selectionModel.hideUnowned = false } else if (this.selectionModel.ownerFilter === OwnerFilterType.NONE) { this.selectionModel.userID = null @@ -132,7 +132,7 @@ export class PermissionsFilterDropdownComponent extends ComponentWithPermissions } else if ( this.selectionModel.ownerFilter === OwnerFilterType.SHARED_BY_ME ) { - this.selectionModel.userID = this.settingsService.currentUser.id + this.selectionModel.userID = this.settingsService.currentUser()?.id this.selectionModel.includeUsers = [] this.selectionModel.excludeUsers = [] this.selectionModel.hideUnowned = false diff --git a/src-ui/src/app/components/common/preview-popup/preview-popup.component.html b/src-ui/src/app/components/common/preview-popup/preview-popup.component.html index e949b7b0b..0bf21029b 100644 --- a/src-ui/src/app/components/common/preview-popup/preview-popup.component.html +++ b/src-ui/src/app/components/common/preview-popup/preview-popup.component.html @@ -1,28 +1,28 @@ + autoClose="true" [popoverClass]="popoverClass()" (mouseenter)="mouseEnterPreview()" (mouseleave)="mouseLeavePreview()" #popover="ngbPopover">
        - @if (error) { + @if (error()) {

        Error loading preview

        } @else { @if (renderAsObject) { - @if (previewText) { -
        {{previewText}}
        + @if (previewText()) { +
        {{previewText()}}
        } @else { } } @else { - @if (requiresPassword) { + @if (requiresPassword()) {
        } - @if (!requiresPassword) { + @if (!requiresPassword()) { { component.popover.open() component.onError({ name: 'PasswordException' }) fixture.detectChanges() - expect(component.requiresPassword).toBeTruthy() + expect(component.requiresPassword()).toBeTruthy() expect(fixture.debugElement.query(By.css('i-bs'))).not.toBeNull() }) @@ -121,16 +121,18 @@ describe('PreviewPopupComponent', () => { ) component.init() expect(httpSpy).toHaveBeenCalled() - expect(component.error).toBeTruthy() + expect(component.error()).toBeTruthy() httpSpy.mockReturnValueOnce(of('Preview text')) component.init() - expect(component.previewText).toEqual('Preview text') + expect(component.previewText()).toEqual('Preview text') }) it('should show preview on mouseover after delay to preload content', () => { component.mouseEnterPreview() expect(component.popover.isOpen()).toBeTruthy() + expect(component.popoverClass()).toContain('opacity-0') jest.advanceTimersByTime(600) + expect(component.popoverClass()).not.toContain('opacity-0') component.close() jest.advanceTimersByTime(600) }) diff --git a/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts b/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts index 9c42d1a61..ae271ffe4 100644 --- a/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts +++ b/src-ui/src/app/components/common/preview-popup/preview-popup.component.ts @@ -1,5 +1,12 @@ import { HttpClient } from '@angular/common/http' -import { Component, inject, Input, OnDestroy, ViewChild } from '@angular/core' +import { + Component, + inject, + Input, + OnDestroy, + signal, + ViewChild, +} from '@angular/core' import { NgbPopover, NgbPopoverModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { first, Subject, takeUntil } from 'rxjs' @@ -55,17 +62,17 @@ export class PreviewPopupComponent implements OnDestroy { unsubscribeNotifier: Subject = new Subject() - error = false + readonly error = signal(false) - requiresPassword: boolean = false + readonly requiresPassword = signal(false) - previewText: string + readonly previewText = signal(null) @ViewChild('popover') popover: NgbPopover - mouseOnPreview: boolean = false + readonly mouseOnPreview = signal(false) - popoverClass: string = 'shadow popover-preview' + readonly popoverClass = signal('shadow popover-preview') get renderAsObject(): boolean { return (this.isPdf && this.useNativePdfViewer) || !this.isPdf @@ -97,10 +104,10 @@ export class PreviewPopupComponent implements OnDestroy { .pipe(first(), takeUntil(this.unsubscribeNotifier)) .subscribe({ next: (res) => { - this.previewText = res.toString() + this.previewText.set(res.toString()) }, error: (err) => { - this.error = err + this.error.set(err) }, }) } @@ -108,22 +115,24 @@ export class PreviewPopupComponent implements OnDestroy { onError(event: any) { if (event.name == 'PasswordException') { - this.requiresPassword = true + this.requiresPassword.set(true) } else { - this.error = true + this.error.set(true) } } mouseEnterPreview() { - this.mouseOnPreview = true + this.mouseOnPreview.set(true) if (!this.popover.isOpen()) { // we're going to open but hide to pre-load content during hover delay this.popover.open() - this.popoverClass = 'shadow popover-preview pe-none opacity-0' + this.popoverClass.set('shadow popover-preview pe-none opacity-0') setTimeout(() => { - if (this.mouseOnPreview) { + if (this.mouseOnPreview()) { // show popover - this.popoverClass = this.popoverClass.replace('pe-none opacity-0', '') + this.popoverClass.set( + this.popoverClass().replace('pe-none opacity-0', '') + ) } else { this.popover.close(true) } @@ -132,13 +141,13 @@ export class PreviewPopupComponent implements OnDestroy { } mouseLeavePreview() { - this.mouseOnPreview = false + this.mouseOnPreview.set(false) } public close(immediate: boolean = false) { setTimeout( () => { - if (!this.mouseOnPreview) this.popover.close() + if (!this.mouseOnPreview()) this.popover.close() }, immediate ? 0 : 300 ) diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index c329beb92..73bad631a 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -7,38 +7,38 @@
        - +
        -
        +
        - +
        - +
        -
        +
        - +
        - - + +
        - Copied! + Copied!
        Warning: changing the token cannot be undone
        @@ -155,10 +155,10 @@ } @@ -179,7 +179,7 @@
        - - + +
        diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts index 101406990..6dcae6c6b 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.spec.ts @@ -1,9 +1,4 @@ -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { Clipboard } from '@angular/cdk/clipboard' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' @@ -191,7 +186,8 @@ describe('ProfileEditDialogComponent', () => { expect(component.saveDisabled).toBeFalsy() }) - it('should logout on save if password changed', fakeAsync(() => { + it('should logout on save if password changed', () => { + jest.useFakeTimers() const getSpy = jest.spyOn(profileService, 'get') getSpy.mockReturnValue(of(profile)) const getProvidersSpy = jest.spyOn( @@ -211,13 +207,15 @@ describe('ProfileEditDialogComponent', () => { .mockImplementation(() => {}) component.save() expect(updateSpy).toHaveBeenCalled() - tick(2600) + jest.advanceTimersByTime(2600) expect(navSpy).toHaveBeenCalledWith( `${window.location.origin}/accounts/logout/?next=/accounts/login/?next=/` ) - })) + jest.useRealTimers() + }) - it('should support auth token copy', fakeAsync(() => { + it('should support auth token copy', () => { + jest.useFakeTimers() const getSpy = jest.spyOn(profileService, 'get') getSpy.mockReturnValue(of(profile)) const getProvidersSpy = jest.spyOn( @@ -229,10 +227,11 @@ describe('ProfileEditDialogComponent', () => { const copySpy = jest.spyOn(clipboard, 'copy') component.copyAuthToken() expect(copySpy).toHaveBeenCalledWith(profile.auth_token) - expect(component.copied).toBeTruthy() - tick(3000) - expect(component.copied).toBeFalsy() - })) + expect(component.copied()).toBeTruthy() + jest.advanceTimersByTime(3000) + expect(component.copied()).toBeFalsy() + jest.useRealTimers() + }) it('should support generate token, display error if needed', () => { const getSpy = jest.spyOn(profileService, 'get') @@ -366,11 +365,13 @@ describe('ProfileEditDialogComponent', () => { expect(component.isTotpEnabled).toBeFalsy() }) - it('should copy recovery codes', fakeAsync(() => { + it('should copy recovery codes', () => { + jest.useFakeTimers() const copySpy = jest.spyOn(clipboard, 'copy') component.recoveryCodes = ['1', '2', '3'] component.copyRecoveryCodes() expect(copySpy).toHaveBeenCalledWith('1\n2\n3') - tick(3000) - })) + jest.advanceTimersByTime(3000) + jest.useRealTimers() + }) }) diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts index d6c839f1e..fd17a26da 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts @@ -1,5 +1,5 @@ import { Clipboard } from '@angular/cdk/clipboard' -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, inject, signal } from '@angular/core' import { FormControl, FormGroup, @@ -50,8 +50,12 @@ export class ProfileEditDialogComponent private toastService = inject(ToastService) private clipboard = inject(Clipboard) - public networkActive: boolean = false - public error: any + readonly networkActive = signal(false) + readonly error = signal(undefined) + readonly showPasswordConfirm = signal(false) + readonly showEmailConfirm = signal(false) + readonly copied = signal(false) + readonly codesCopied = signal(false) public form = new FormGroup({ email: new FormControl(''), @@ -67,23 +71,18 @@ export class ProfileEditDialogComponent private currentPassword: string private newPassword: string private passwordConfirm: string - public showPasswordConfirm: boolean = false + public hasUsablePassword: boolean = false private currentEmail: string private newEmail: string private emailConfirm: string - public showEmailConfirm: boolean = false public isTotpEnabled: boolean = false public totpSettings: TotpSettings public totpSettingsLoading: boolean = false public totpLoading: boolean = false public recoveryCodes: string[] - - public copied: boolean = false - public codesCopied: boolean = false - public socialAccounts: SocialAccount[] = [] public socialAccountProviders: SocialAccountProvider[] = [] @@ -95,12 +94,12 @@ export class ProfileEditDialogComponent } ngOnInit(): void { - this.networkActive = true + this.networkActive.set(true) this.profileService .get() .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((profile) => { - this.networkActive = false + this.networkActive.set(false) this.form.patchValue(profile) this.currentEmail = profile.email this.form.get('email').valueChanges.subscribe((newEmail) => { @@ -126,7 +125,7 @@ export class ProfileEditDialogComponent } get saveDisabled(): boolean { - return this.error?.password_confirm || this.error?.email_confirm + return this.error()?.password_confirm || this.error()?.email_confirm } onEmailKeyUp(event: KeyboardEvent): void { @@ -140,18 +139,17 @@ export class ProfileEditDialogComponent } onEmailChange(): void { - this.showEmailConfirm = this.currentEmail !== this.newEmail - if (this.showEmailConfirm) { + this.showEmailConfirm.set(this.currentEmail !== this.newEmail) + if (this.showEmailConfirm()) { this.form.get('email_confirm').enable() if (this.newEmail !== this.emailConfirm) { - if (!this.error) this.error = {} - this.error.email_confirm = $localize`Emails must match` + this.setFieldError('email_confirm', $localize`Emails must match`) } else { - delete this.error?.email_confirm + this.clearFieldError('email_confirm') } } else { this.form.get('email_confirm').disable() - delete this.error?.email_confirm + this.clearFieldError('email_confirm') } } @@ -167,29 +165,42 @@ export class ProfileEditDialogComponent } onPasswordChange(): void { - this.showPasswordConfirm = this.currentPassword !== this.newPassword + this.showPasswordConfirm.set(this.currentPassword !== this.newPassword) - if (this.showPasswordConfirm) { + if (this.showPasswordConfirm()) { this.form.get('password_confirm').enable() if (this.newPassword !== this.passwordConfirm) { - if (!this.error) this.error = {} - this.error.password_confirm = $localize`Passwords must match` + this.setFieldError('password_confirm', $localize`Passwords must match`) } else { - delete this.error?.password_confirm + this.clearFieldError('password_confirm') } } else { this.form.get('password_confirm').disable() - delete this.error?.password_confirm + this.clearFieldError('password_confirm') } } + private setFieldError(fieldName: string, message: string): void { + this.error.set({ + ...this.error(), + [fieldName]: message, + }) + } + + private clearFieldError(fieldName: string): void { + if (!this.error()) return + const error = { ...this.error() } + delete error[fieldName] + this.error.set(Object.keys(error).length ? error : undefined) + } + save(): void { const passwordChanged = this.newPassword && this.currentPassword !== this.newPassword const profile = Object.assign({}, this.form.value) delete profile.totp_code - this.error = null - this.networkActive = true + this.error.set(null) + this.networkActive.set(true) this.profileService .update(profile) .pipe(takeUntil(this.unsubscribeNotifier)) @@ -210,8 +221,8 @@ export class ProfileEditDialogComponent }, error: (error) => { this.toastService.showError($localize`Error saving profile`, error) - this.error = error?.error - this.networkActive = false + this.error.set(error?.error) + this.networkActive.set(false) }, }) } @@ -236,9 +247,9 @@ export class ProfileEditDialogComponent copyAuthToken(): void { this.clipboard.copy(this.form.get('auth_token').value) - this.copied = true + this.copied.set(true) setTimeout(() => { - this.copied = false + this.copied.set(false) }, 3000) } @@ -330,9 +341,9 @@ export class ProfileEditDialogComponent public copyRecoveryCodes(): void { this.clipboard.copy(this.recoveryCodes.join('\n')) - this.codesCopied = true + this.codesCopied.set(true) setTimeout(() => { - this.codesCopied = false + this.codesCopied.set(false) }, 3000) } } diff --git a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html index b7fed28e1..2814e1cf1 100644 --- a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html +++ b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.html @@ -8,18 +8,18 @@

        Selected documents: - {{ selectionCount }} + {{ selectionCount() }}

        - @if (documentPreview.length > 0) { + @if (documentPreview().length > 0) {
          - @for (doc of documentPreview; track doc.id) { + @for (doc of documentPreview(); track doc.id) {
        • {{ doc.title | documentTitle }}
        • } - @if (selectionCount > documentPreview.length) { + @if (selectionCount() > documentPreview().length) {
        • - + {{ selectionCount - documentPreview.length }} more… + + {{ selectionCount() - documentPreview().length }} more…
        • }
        @@ -72,10 +72,10 @@ type="button" (click)="copy(createdBundle)" > - @if (copied) { + @if (copied()) { } - @if (!copied) { + @if (!copied()) { } Copy link @@ -118,8 +118,8 @@ type="button" class="btn btn-primary btn-sm d-inline-flex align-items-center gap-2 text-nowrap" (click)="submit()" - [disabled]="loading || !buttonsEnabled"> - @if (loading) { + [disabled]="loading() || !buttonsEnabled"> + @if (loading()) { } {{ btnCaption }} diff --git a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.spec.ts b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.spec.ts index da4d93c6a..d7c695131 100644 --- a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.spec.ts @@ -1,10 +1,5 @@ import { Clipboard } from '@angular/cdk/clipboard' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' import { FileVersion } from 'src/app/data/share-link' @@ -58,10 +53,10 @@ describe('ShareLinkBundleDialogComponent', () => { it('builds payload and emits confirm on submit', () => { const confirmSpy = jest.spyOn(component.confirmClicked, 'emit') - component.documents = [ + component.setDocuments([ { id: 1, title: 'Doc 1' } as any, { id: 2, title: 'Doc 2' } as any, - ] + ]) component.form.setValue({ shareArchiveVersion: false, expirationDays: 3, @@ -101,14 +96,15 @@ describe('ShareLinkBundleDialogComponent', () => { const docs = Array.from({ length: 12 }).map((_, index) => ({ id: index + 1, })) - component.documents = docs as any + component.setDocuments(docs as any) - expect(component.selectionCount).toBe(12) - expect(component.documentPreview).toHaveLength(10) - expect(component.documentPreview[0].id).toBe(1) + expect(component.selectionCount()).toBe(12) + expect(component.documentPreview()).toHaveLength(10) + expect(component.documentPreview()[0].id).toBe(1) }) - it('copies share link and resets state after timeout', fakeAsync(() => { + it('copies share link and resets state after timeout', () => { + jest.useFakeTimers() const copySpy = jest.spyOn(clipboard, 'copy').mockReturnValue(true) const bundle = { slug: 'bundle-slug', @@ -118,12 +114,13 @@ describe('ShareLinkBundleDialogComponent', () => { component.copy(bundle) expect(copySpy).toHaveBeenCalledWith(component.getShareUrl(bundle)) - expect(component.copied).toBe(true) + expect(component.copied()).toBe(true) expect(toastService.showInfo).toHaveBeenCalled() - tick(3000) - expect(component.copied).toBe(false) - })) + jest.advanceTimersByTime(3000) + expect(component.copied()).toBe(false) + jest.useRealTimers() + }) it('generates share URLs based on API base URL', () => { environment.apiBaseUrl = 'https://example.com/api/' diff --git a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.ts b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.ts index 37aa70950..903e572c3 100644 --- a/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.ts +++ b/src-ui/src/app/components/common/share-link-bundle-dialog/share-link-bundle-dialog.component.ts @@ -1,6 +1,6 @@ import { Clipboard } from '@angular/cdk/clipboard' import { CommonModule } from '@angular/common' -import { Component, Input, inject } from '@angular/core' +import { Component, inject, signal } from '@angular/core' import { FormBuilder, FormGroup, ReactiveFormsModule } from '@angular/forms' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { Document } from 'src/app/data/document' @@ -38,10 +38,11 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent { private readonly clipboard = inject(Clipboard) private readonly toastService = inject(ToastService) - private _documents: Document[] = [] + readonly documents = signal([]) + readonly selectionCount = signal(0) + readonly documentPreview = signal([]) + readonly copied = signal(false) - selectionCount = 0 - documentPreview: Document[] = [] form: FormGroup = this.formBuilder.group({ shareArchiveVersion: true, expirationDays: [7], @@ -51,28 +52,27 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent { readonly expirationOptions = SHARE_LINK_EXPIRATION_OPTIONS createdBundle: ShareLinkBundleSummary | null = null - copied = false onOpenManage?: () => void readonly statuses = ShareLinkBundleStatus constructor() { super() - this.loading = false + this.loading.set(false) this.title = $localize`Create share link bundle` this.btnCaption = $localize`Create link` } - @Input() - set documents(docs: Document[]) { - this._documents = docs.concat() - this.selectionCount = this._documents.length - this.documentPreview = this._documents.slice(0, 10) + setDocuments(docs: Document[]) { + const documents = docs.concat() + this.documents.set(documents) + this.selectionCount.set(documents.length) + this.documentPreview.set(documents.slice(0, 10)) } submit() { if (this.createdBundle) return this.payload = { - document_ids: this._documents.map((doc) => doc.id), + document_ids: this.documents().map((doc) => doc.id), file_version: this.form.value.shareArchiveVersion ? FileVersion.Archive : FileVersion.Original, @@ -92,10 +92,10 @@ export class ShareLinkBundleDialogComponent extends ConfirmDialogComponent { copy(bundle: ShareLinkBundleSummary): void { const success = this.clipboard.copy(this.getShareUrl(bundle)) if (success) { - this.copied = true + this.copied.set(true) this.toastService.showInfo($localize`Share link copied to clipboard.`) setTimeout(() => { - this.copied = false + this.copied.set(false) }, 3000) } } diff --git a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html index 2f2155412..15b5bc9de 100644 --- a/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html +++ b/src-ui/src/app/components/common/share-link-bundle-manage-dialog/share-link-bundle-manage-dialog.component.html @@ -4,27 +4,27 @@
        - @if (loading) { + @if (loading()) {
        Loading share link bundles…
        } - @if (!loading && error) { + @if (!loading() && error()) {
        - {{ error }} + {{ error() }}
        } - @if (!loading && !error) { + @if (!loading() && !error()) {

        Status updates every few seconds while bundles are being prepared.

        - @if (bundles.length === 0) { + @if (bundles().length === 0) {

        No share link bundles currently exist.

        } - @if (bundles.length > 0) { + @if (bundles().length > 0) {
        @@ -39,7 +39,7 @@ - @for (bundle of bundles; track bundle.id) { + @for (bundle of bundles(); track bundle.id) { - - - + + + @for (column of extraColumns; track column) { - + } - @if (loading && data.length === 0) { + @if (loading() && data().length === 0) { } - @for (object of data; track object) { + @for (object of data(); track object) { }
        {{ bundle.created | date: 'short' }}
        @@ -113,10 +113,10 @@ title="Copy share link" i18n-title > - @if (copiedSlug === bundle.slug) { + @if (copiedSlug() === bundle.slug) { } - @if (copiedSlug !== bundle.slug) { + @if (copiedSlug() !== bundle.slug) { } Copy share link @@ -125,7 +125,7 @@
          - @if (!shareLinks || shareLinks.length === 0) { + @if (!shareLinks() || shareLinks().length === 0) {
        • No existing links
        • } - @for (link of shareLinks; track link) { + @for (link of shareLinks(); track link) {
        • @@ -19,10 +19,10 @@ }
          - Copied! + Copied!
        • }
        @@ -44,7 +44,7 @@
        - +
        @@ -55,11 +55,11 @@ } - - @if (aiEnabled) { + @if (aiEnabled()) {
        -
        - @if (!suggestions?.suggested_tags && !suggestions?.suggested_document_types && !suggestions?.suggested_correspondents) { + @if (!suggestions()?.suggested_tags && !suggestions()?.suggested_document_types && !suggestions()?.suggested_correspondents) {
        No novel suggestions
        } - @if (suggestions?.suggested_tags.length > 0) { + @if (suggestions()?.suggested_tags.length > 0) { Tags - @for (tag of suggestions.suggested_tags; track tag) { + @for (tag of suggestions().suggested_tags; track tag) { } } - @if (suggestions?.suggested_document_types.length > 0) { + @if (suggestions()?.suggested_document_types.length > 0) {
        Document Types
        - @for (type of suggestions.suggested_document_types; track type) { + @for (type of suggestions().suggested_document_types; track type) { } } - @if (suggestions?.suggested_correspondents.length > 0) { + @if (suggestions()?.suggested_correspondents.length > 0) {
        Correspondents
        - @for (correspondent of suggestions.suggested_correspondents; track correspondent) { + @for (correspondent of suggestions().suggested_correspondents; track correspondent) { } } diff --git a/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.spec.ts b/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.spec.ts index 863393ace..ffb63b420 100644 --- a/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.spec.ts +++ b/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.spec.ts @@ -22,25 +22,25 @@ describe('SuggestionsDropdownComponent', () => { }) it('should calculate totalSuggestions', () => { - component.suggestions = { + fixture.componentRef.setInput('suggestions', { suggested_correspondents: ['John Doe'], suggested_tags: ['Tag1', 'Tag2'], suggested_document_types: ['Type1'], - } + }) expect(component.totalSuggestions).toBe(4) }) it('should emit getSuggestions when clickSuggest is called and suggestions are null', () => { jest.spyOn(component.getSuggestions, 'emit') - component.suggestions = null + fixture.componentRef.setInput('suggestions', null) component.clickSuggest() expect(component.getSuggestions.emit).toHaveBeenCalled() }) it('should not emit getSuggestions when disabled', () => { jest.spyOn(component.getSuggestions, 'emit') - component.disabled = true - component.suggestions = null + fixture.componentRef.setInput('disabled', true) + fixture.componentRef.setInput('suggestions', null) fixture.detectChanges() component.clickSuggest() @@ -50,13 +50,13 @@ describe('SuggestionsDropdownComponent', () => { }) it('should toggle dropdown when clickSuggest is called and suggestions are not null', () => { - component.aiEnabled = true + fixture.componentRef.setInput('aiEnabled', true) fixture.detectChanges() - component.suggestions = { + fixture.componentRef.setInput('suggestions', { suggested_correspondents: [], suggested_tags: [], suggested_document_types: [], - } + }) component.clickSuggest() expect(component.dropdown.open).toBeTruthy() }) diff --git a/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.ts b/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.ts index 6cf63f683..5c4c54e06 100644 --- a/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.ts +++ b/src-ui/src/app/components/common/suggestions-dropdown/suggestions-dropdown.component.ts @@ -1,9 +1,9 @@ import { Component, EventEmitter, - Input, Output, ViewChild, + input, } from '@angular/core' import { NgbDropdown, NgbDropdownModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' @@ -20,18 +20,10 @@ export class SuggestionsDropdownComponent { public popperOptions = pngxPopperOptions @ViewChild('dropdown') dropdown: NgbDropdown - - @Input() - suggestions: DocumentSuggestions = null - - @Input() - aiEnabled: boolean = false - - @Input() - loading: boolean = false - - @Input() - disabled: boolean = false + readonly suggestions = input(null) + readonly aiEnabled = input(false) + readonly loading = input(false) + readonly disabled = input(false) @Output() getSuggestions: EventEmitter = @@ -48,14 +40,14 @@ export class SuggestionsDropdownComponent { public clickSuggest(): void { if ( - this.disabled || - this.loading || - (this.suggestions && !this.aiEnabled) + this.disabled() || + this.loading() || + (this.suggestions() && !this.aiEnabled()) ) { return } - if (!this.suggestions) { + if (!this.suggestions()) { this.getSuggestions.emit(this) } else { this.dropdown?.toggle() @@ -64,9 +56,9 @@ export class SuggestionsDropdownComponent { get totalSuggestions(): number { return ( - this.suggestions?.suggested_correspondents?.length + - this.suggestions?.suggested_tags?.length + - this.suggestions?.suggested_document_types?.length || 0 + this.suggestions()?.suggested_correspondents?.length + + this.suggestions()?.suggested_tags?.length + + this.suggestions()?.suggested_document_types?.length || 0 ) } } diff --git a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html index 5422c875e..badd44b5d 100644 --- a/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html +++ b/src-ui/src/app/components/common/system-status-dialog/system-status-dialog.component.html @@ -3,7 +3,7 @@
        - @if (!status) { + @if (!status()) {
        @@ -21,25 +21,25 @@
        Paperless-ngx Version
        - {{status.pngx_version}} - @if (versionMismatch) { + {{status().pngx_version}} + @if (versionMismatch()) { } Frontend version: {{frontendVersion}}
        - Backend version: {{status.pngx_version}} + Backend version: {{status().pngx_version}}
        Install Type
        -
        {{status.install_type}}
        +
        {{status().install_type}}
        Server OS
        -
        {{status.server_os}}
        +
        {{status().server_os}}
        Media Storage
        - - {{status.storage.available | fileSize}} available ({{status.storage.total | fileSize}} total) + + {{status().storage.available | fileSize}} available ({{status().storage.total | fileSize}} total)
        @@ -54,39 +54,39 @@
        Type
        -
        {{status.database.type}}
        +
        {{status().database.type}}
        Status
        - @if (status.database.status === 'OK') { - {{status.database.url}} + @if (status().database.status === 'OK') { + {{status().database.url}} } @else { - {{status.database.url}}: {{status.database.error}} + {{status().database.url}}: {{status().database.error}} }
        Migration Status
        - @if (status.tasks.redis_status === 'OK') { - {{status.tasks.redis_url}} + @if (status().tasks.redis_status === 'OK') { + {{status().tasks.redis_url}} } @else { - {{status.tasks.redis_url}}: {{status.tasks.redis_error}} + {{status().tasks.redis_url}}: {{status().tasks.redis_error}} }
        Celery Status
        - @if (status.tasks.celery_status === 'OK') { - {{status.tasks.celery_url}} + @if (status().tasks.celery_status === 'OK') { + {{status().tasks.celery_url}} } @else { - {{status.tasks.celery_error}} + {{status().tasks.celery_error}} }
        -
        Recent Task Activity ({{status.tasks.summary.days}} days)
        +
        Recent Task Activity ({{status().tasks.summary.days}} days)
        - @if (status.tasks.summary.total_count > 0) { + @if (status().tasks.summary.total_count > 0) {
        • Total: - {{status.tasks.summary.total_count}} + {{status().tasks.summary.total_count}}
        • Successful: - {{status.tasks.summary.success_count}} + {{status().tasks.summary.success_count}}
        • Failed: - {{status.tasks.summary.failure_count}} + {{status().tasks.summary.failure_count}}
        • Pending: - {{status.tasks.summary.pending_count}} + {{status().tasks.summary.pending_count}}
        } @else { @@ -184,8 +184,8 @@
        Search Index
        - @if (status.tasks.index_status === 'OK') { -
        Last Updated:
        {{status.tasks.index_last_modified | customDate:'medium'}} + @if (status().tasks.index_status === 'OK') { +
        Last Updated:
        {{status().tasks.index_last_modified | customDate:'medium'}} } @else { -
        Error:
        {{status.tasks.index_error}} +
        Error:
        {{status().tasks.index_error}} }
        Classifier
        @if (currentUserIsSuperUser) { @@ -227,26 +227,26 @@ }
        - @if (status.tasks.classifier_status === 'OK') { -
        Last Trained:
        {{status.tasks.classifier_last_trained | customDate:'medium'}} + @if (status().tasks.classifier_status === 'OK') { +
        Last Trained:
        {{status().tasks.classifier_last_trained | customDate:'medium'}} } @else { -
        Error:
        {{status.tasks.classifier_error}} +
        Error:
        {{status().tasks.classifier_error}} }
        Sanity Checker
        @if (currentUserIsSuperUser) { @@ -261,16 +261,16 @@ }
        - @if (status.tasks.sanity_check_status === 'OK') { -
        Last Run:
        {{status.tasks.sanity_check_last_run | customDate:'medium'}} + @if (status().tasks.sanity_check_status === 'OK') { +
        Last Run:
        {{status().tasks.sanity_check_last_run | customDate:'medium'}} } @else { -
        Error:
        {{status.tasks.sanity_check_error}} +
        Error:
        {{status().tasks.sanity_check_error}} }
        WebSocket Connection
        - @if (status.websocket_connected === 'OK') { + @if (status().websocket_connected === 'OK') { OK } @else { @@ -283,18 +283,18 @@
        AI Index
        @if (currentUserIsSuperUser) { @@ -309,10 +309,10 @@ }
        - @if (status.tasks.llmindex_status === 'OK') { -
        Last Run:
        {{status.tasks.llmindex_last_modified | customDate:'medium'}} + @if (status().tasks.llmindex_status === 'OK') { +
        Last Run:
        {{status().tasks.llmindex_last_modified | customDate:'medium'}} } @else { -
        Error:
        {{status.tasks.llmindex_error}} +
        Error:
        {{status().tasks.llmindex_error}} }
        } @@ -325,10 +325,10 @@
        -
        - +
        - @if (metadata?.has_archive_version) { - + @if (metadata()?.has_archive_version) { +
        }
        @@ -146,9 +146,9 @@
        @@ -172,33 +172,33 @@
        -
          +
          • Details
            - + @if (!isFieldHidden(DocumentDetailFieldID.ArchiveSerialNumber)) { - + } - + @if (!isFieldHidden(DocumentDetailFieldID.Correspondent)) { - + } @if (!isFieldHidden(DocumentDetailFieldID.DocumentType)) { - + } @if (!isFieldHidden(DocumentDetailFieldID.StoragePath)) { - + } @if (!isFieldHidden(DocumentDetailFieldID.Tags)) { - + } - @for (fieldInstance of document?.custom_fields; track fieldInstance.field; let i = $index) { + @for (fieldInstance of document()?.custom_fields; track fieldInstance.field; let i = $index) {
            @switch (getCustomFieldFromInstance(fieldInstance)?.data_type) { @case (CustomFieldDataType.String) { @@ -263,7 +263,7 @@ @case (CustomFieldDataType.DocumentLink) { Metadata - @if (document) { + @if (document()) { - + - + - + - + - + - + - + - @if (metadata?.has_archive_version) { + @if (metadata()?.has_archive_version) { - + } - @if (metadata?.has_archive_version) { + @if (metadata()?.has_archive_version) { - + }
            Date modified{{document.modified | customDate}}{{document().modified | customDate}}
            Date added{{document.added | customDate}}{{document().added | customDate}}
            Media filename{{metadata?.media_filename}}{{metadata()?.media_filename}}
            Original filename{{metadata?.original_filename}}{{metadata()?.original_filename}}
            Original MD5 checksum{{metadata?.original_checksum}}{{metadata()?.original_checksum}}
            Original file size{{metadata?.original_size | fileSize}}{{metadata()?.original_size | fileSize}}
            Original mime type{{metadata?.original_mime_type}}{{metadata()?.original_mime_type}}
            Archive MD5 checksum{{metadata?.archive_checksum}}{{metadata()?.archive_checksum}}
            Archive file size{{metadata?.archive_size | fileSize}}{{metadata()?.archive_size | fileSize}}
            } - @if (metadata?.original_metadata?.length > 0) { - + @if (metadata()?.original_metadata?.length > 0) { + } - @if (metadata?.archive_metadata?.length > 0) { - + @if (metadata()?.archive_metadata?.length > 0) { + }
            @@ -382,11 +382,11 @@ @if (notesEnabled) {
          • - Notes @if (document?.notes.length) { - {{document.notes.length}} + Notes @if (document()?.notes.length) { + {{document().notes.length}} } - +
          • } @@ -396,7 +396,7 @@ History
            - +
            @@ -407,23 +407,23 @@ Permissions
            - +
            } - @if (document?.duplicate_documents?.length) { + @if (document()?.duplicate_documents?.length) {
          • Duplicates - {{ document.duplicate_documents.length }} + {{ document().duplicate_documents.length }}
            Duplicate documents detected:
            - @for (duplicate of document.duplicate_documents; track duplicate.id) { + @for (duplicate of document()?.duplicate_documents; track duplicate.id) {
            - + @if (hasNext()) { - + + } @else { + } - @if (!hasNext()) { - - } - +
            -
            +
            @if (showThumbnailOverlay) { - Document loading... + Document loading... }
            @@ -483,44 +482,49 @@
            - @if (document) { + @if (document()) { @switch (archiveContentRenderType) { @case (ContentRenderType.PDF) { @if (!useNativePdfViewer) {
            } @else { - + + Preview is unavailable. + } } @case (ContentRenderType.Text) { -
            {{previewText}}
            +
            {{previewText()}}
            } @case (ContentRenderType.Image) {
            - {{title}} + {{title()}}
            } @case (ContentRenderType.TIFF) { - @if (!tiffError) { + @if (!tiffError()) {
            - {{title}} + {{title()}}
            } @else { -
            {{tiffError}}
            +
            {{tiffError()}}
            } } @case (ContentRenderType.Other) { - + + Preview is unavailable. + } } @if (requiresPassword) { diff --git a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts index 8fea33f27..46a9307f2 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.spec.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.spec.ts @@ -9,13 +9,7 @@ import { HttpTestingController, provideHttpClientTesting, } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - discardPeriodicTasks, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { ActivatedRoute, @@ -279,6 +273,7 @@ describe('DocumentDetailComponent', () => { }).compileComponents() router = TestBed.inject(Router) + jest.spyOn(router, 'navigate').mockResolvedValue(true) activatedRoute = TestBed.inject(ActivatedRoute) openDocumentsService = TestBed.inject(OpenDocumentsService) documentService = TestBed.inject(DocumentService) @@ -286,7 +281,7 @@ describe('DocumentDetailComponent', () => { toastService = TestBed.inject(ToastService) documentListViewService = TestBed.inject(DocumentListViewService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) customFieldsService = TestBed.inject(CustomFieldsService) deviceDetectorService = TestBed.inject(DeviceDetectorService) fixture = TestBed.createComponent(DocumentDetailComponent) @@ -295,6 +290,10 @@ describe('DocumentDetailComponent', () => { component = fixture.componentInstance }) + afterEach(() => { + jest.useRealTimers() + }) + function initNormally() { jest .spyOn(activatedRoute, 'paramMap', 'get') @@ -325,10 +324,13 @@ describe('DocumentDetailComponent', () => { .spyOn(openDocumentsService, 'openDocument') .mockReturnValueOnce(of(true)) fixture.detectChanges() - expect(component.activeNavID).toEqual(component.DocumentDetailNavIDs.Notes) + expect(component.activeNavID()).toEqual( + component.DocumentDetailNavIDs.Notes + ) }) - it('should switch from preview to details when pdf preview enters the DOM', fakeAsync(() => { + it('should switch from preview to details when pdf preview enters the DOM', () => { + jest.useFakeTimers() component.nav = { activeId: component.DocumentDetailNavIDs.Preview, select: jest.fn(), @@ -337,11 +339,11 @@ describe('DocumentDetailComponent', () => { nativeElement: { offsetParent: {} }, } - tick() + jest.advanceTimersByTime(0) expect(component.nav.select).toHaveBeenCalledWith( component.DocumentDetailNavIDs.Details ) - })) + }) it('should forward title key up value to titleSubject', () => { const subjectSpy = jest.spyOn(component.titleSubject, 'next') @@ -375,28 +377,29 @@ describe('DocumentDetailComponent', () => { }) }) - it('should update title after debounce', fakeAsync(() => { + it('should update title after debounce', () => { + jest.useFakeTimers() initNormally() component.titleInput.value = 'Foo Bar' component.titleSubject.next('Foo Bar') - tick(1000) + jest.advanceTimersByTime(1000) expect(component.documentForm.get('title').value).toEqual('Foo Bar') - discardPeriodicTasks() - })) + }) - it('should update title before doc change if was not updated via debounce', fakeAsync(() => { + it('should update title before doc change if was not updated via debounce', () => { + jest.useFakeTimers() initNormally() component.titleInput.value = 'Foo Bar' component.titleInput.inputField.nativeElement.dispatchEvent( new Event('change') ) - tick(1000) + jest.advanceTimersByTime(1000) expect(component.documentForm.get('title').value).toEqual('Foo Bar') - })) + }) it('should load non-open document via param', () => { initNormally() - expect(component.document).toEqual(doc) + expect(component.document()).toEqual(doc) }) it('should redirect to root when opening a version document id', () => { @@ -423,7 +426,7 @@ describe('DocumentDetailComponent', () => { ) fixture.detectChanges() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') expect(getRootSpy).toHaveBeenCalledWith(10) expect(navigateSpy).toHaveBeenCalledWith(['documents', 3, 'details'], { @@ -455,7 +458,7 @@ describe('DocumentDetailComponent', () => { ) fixture.detectChanges() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') expect(navigateSpy).toHaveBeenCalledWith(['404'], { replaceUrl: true }) }) @@ -501,7 +504,7 @@ describe('DocumentDetailComponent', () => { ) fixture.detectChanges() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') fixture.detectChanges() const deleteButtons = fixture.debugElement.queryAll( @@ -512,12 +515,12 @@ describe('DocumentDetailComponent', () => { it('should fall back to details tab when duplicates tab is active but no duplicates', () => { initNormally() - component.activeNavID = component.DocumentDetailNavIDs.Duplicates + component.activeNavID.set(component.DocumentDetailNavIDs.Duplicates) const noDupDoc = { ...doc, duplicate_documents: [] } component.updateComponent(noDupDoc) - expect(component.activeNavID).toEqual( + expect(component.activeNavID()).toEqual( component.DocumentDetailNavIDs.Details ) }) @@ -534,7 +537,7 @@ describe('DocumentDetailComponent', () => { }) ) fixture.detectChanges() // calls ngOnInit - expect(component.document).toEqual(doc) + expect(component.document()).toEqual(doc) }) it('should update cached open document duplicates when reloading an open doc', () => { @@ -578,10 +581,10 @@ describe('DocumentDetailComponent', () => { it('should not attempt to retrieve objects if user does not have permissions', () => { currentUserCan = false initNormally() - expect(component.correspondents).toBeUndefined() - expect(component.documentTypes).toBeUndefined() - expect(component.storagePaths).toBeUndefined() - expect(component.users).toBeUndefined() + expect(component.correspondents()).toBeUndefined() + expect(component.documentTypes()).toBeUndefined() + expect(component.storagePaths()).toBeUndefined() + expect(component.users()).toBeUndefined() httpTestingController.expectNone(`${environment.apiBaseUrl}documents/tags/`) httpTestingController.expectNone( `${environment.apiBaseUrl}documents/correspondents/` @@ -597,9 +600,9 @@ describe('DocumentDetailComponent', () => { it('should support creating tag, remove from suggestions', () => { initNormally() - component.suggestions = { + component.suggestions.set({ suggested_tags: ['Tag1', 'NewTag12'], - } + }) let openModal: NgbModalRef modalService.activeInstances.subscribe((modal) => (openModal = modal[0])) const modalSpy = jest.spyOn(modalService, 'open') @@ -613,14 +616,14 @@ describe('DocumentDetailComponent', () => { text_color: '#000000', }) expect(component.tagsInput.value).toContain(12) - expect(component.suggestions.suggested_tags).not.toContain('NewTag12') + expect(component.suggestions().suggested_tags).not.toContain('NewTag12') }) it('should support creating document type, remove from suggestions', () => { initNormally() - component.suggestions = { + component.suggestions.set({ suggested_document_types: ['DocumentType1', 'NewDocType2'], - } + }) let openModal: NgbModalRef modalService.activeInstances.subscribe((modal) => (openModal = modal[0])) const modalSpy = jest.spyOn(modalService, 'open') @@ -628,16 +631,16 @@ describe('DocumentDetailComponent', () => { expect(modalSpy).toHaveBeenCalled() openModal.componentInstance.succeeded.next({ id: 12, name: 'NewDocType12' }) expect(component.documentForm.get('document_type').value).toEqual(12) - expect(component.suggestions.suggested_document_types).not.toContain( + expect(component.suggestions().suggested_document_types).not.toContain( 'NewDocType2' ) }) it('should support creating correspondent, remove from suggestions', () => { initNormally() - component.suggestions = { + component.suggestions.set({ suggested_correspondents: ['Correspondent1', 'NewCorrrespondent12'], - } + }) let openModal: NgbModalRef modalService.activeInstances.subscribe((modal) => (openModal = modal[0])) const modalSpy = jest.spyOn(modalService, 'open') @@ -648,7 +651,7 @@ describe('DocumentDetailComponent', () => { name: 'NewCorrrespondent12', }) expect(component.documentForm.get('correspondent').value).toEqual(12) - expect(component.suggestions.suggested_correspondents).not.toContain( + expect(component.suggestions().suggested_correspondents).not.toContain( 'NewCorrrespondent12' ) }) @@ -669,15 +672,15 @@ describe('DocumentDetailComponent', () => { it('should allow dischard changes', () => { initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') fixture.detectChanges() jest.spyOn(documentService, 'get').mockReturnValueOnce(of(doc)) component.discard() fixture.detectChanges() - expect(component.title).toEqual(doc.title) + expect(component.title()).toEqual(doc.title) expect(openDocumentsService.hasDirty()).toBeFalsy() // this time with error, mostly for coverage - component.title = 'Foo Bar' + component.title.set('Foo Bar') fixture.detectChanges() const navigateSpy = jest.spyOn(router, 'navigate') jest @@ -694,10 +697,10 @@ describe('DocumentDetailComponent', () => { getSpy.mockClear() getSpy.mockReturnValueOnce(of(doc)) - component.selectedVersionId = 10 + component.selectedVersionId.set(10) component.discard() - expect(getSpy).toHaveBeenCalledWith(component.documentId, 10) + expect(getSpy).toHaveBeenCalledWith(component.documentId(), 10) }) it('should 404 on invalid id', () => { @@ -724,7 +727,7 @@ describe('DocumentDetailComponent', () => { it('should support save, close and show success toast', () => { initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') const closeSpy = jest.spyOn(component, 'close') const patchSpy = jest.spyOn(documentService, 'patch') const toastSpy = jest.spyOn(toastService, 'showInfo') @@ -739,7 +742,7 @@ describe('DocumentDetailComponent', () => { it('should support save without close and show success toast', () => { initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') const closeSpy = jest.spyOn(component, 'close') const patchSpy = jest.spyOn(documentService, 'patch') const toastSpy = jest.spyOn(toastService, 'showInfo') @@ -754,7 +757,7 @@ describe('DocumentDetailComponent', () => { it('save should target currently selected version', () => { initNormally() - component.selectedVersionId = 10 + component.selectedVersionId.set(10) const patchSpy = jest.spyOn(documentService, 'patch') patchSpy.mockReturnValue(of(doc)) @@ -767,7 +770,7 @@ describe('DocumentDetailComponent', () => { it('should show toast error on save if error occurs', () => { currentUserHasObjectPermissions = true initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') const closeSpy = jest.spyOn(component, 'close') const patchSpy = jest.spyOn(documentService, 'patch') const toastSpy = jest.spyOn(toastService, 'showError') @@ -785,7 +788,7 @@ describe('DocumentDetailComponent', () => { it('should show error toast on save but close if user can no longer edit', () => { currentUserHasObjectPermissions = false initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') const closeSpy = jest.spyOn(component, 'close') const patchSpy = jest.spyOn(documentService, 'patch') const toastSpy = jest.spyOn(toastService, 'showInfo') @@ -803,7 +806,7 @@ describe('DocumentDetailComponent', () => { it('should allow save and next', () => { initNormally() const nextDocId = 100 - component.title = 'Foo Bar' + component.title.set('Foo Bar') const patchSpy = jest.spyOn(documentService, 'patch') patchSpy.mockReturnValue(of(doc)) const nextSpy = jest.spyOn(documentListViewService, 'getNext') @@ -821,7 +824,7 @@ describe('DocumentDetailComponent', () => { it('should show toast error on save & next if error occurs', () => { currentUserHasObjectPermissions = true initNormally() - component.title = 'Foo Bar' + component.title.set('Foo Bar') const closeSpy = jest.spyOn(component, 'close') const patchSpy = jest.spyOn(documentService, 'patch') const toastSpy = jest.spyOn(toastService, 'showError') @@ -853,6 +856,7 @@ describe('DocumentDetailComponent', () => { .find((b) => b.nativeElement.textContent === 'Save & next') ).toBeUndefined() nextSpy.mockReturnValue(true) + component.networkActive.set(true) fixture.detectChanges() expect( fixture.debugElement @@ -1014,7 +1018,7 @@ describe('DocumentDetailComponent', () => { it('should support Enter key in password field', () => { initNormally() - component.metadata = { has_archive_version: true } + component.metadata.set({ has_archive_version: true }) component.onError({ name: 'PasswordException' }) // normally dispatched by pdf viewer fixture.detectChanges() expect(component.password).toBeUndefined() @@ -1029,45 +1033,44 @@ describe('DocumentDetailComponent', () => { it('should update n pages after pdf loaded', () => { initNormally() component.pdfPreviewLoaded({ numPages: 1000 } as any) - expect(component.previewNumPages).toEqual(1000) + expect(component.previewNumPages()).toEqual(1000) }) - it('should include delay of 300ms after previewloaded before showing pdf', fakeAsync(() => { + it('should mark preview loaded after pdf loads', () => { initNormally() - expect(component.previewLoaded).toBeFalsy() + expect(component.previewLoaded()).toBeFalsy() component.pdfPreviewLoaded({ numPages: 1000 } as any) - expect(component.previewNumPages).toEqual(1000) - tick(300) - expect(component.previewLoaded).toBeTruthy() - })) + expect(component.previewNumPages()).toEqual(1000) + expect(component.previewLoaded()).toBeTruthy() + }) it('should support zoom controls', () => { initNormally() component.setZoom(PdfZoomLevel.One) // from select - expect(component.previewZoomSetting).toEqual('1') + expect(component.previewZoomSetting()).toEqual('1') component.increaseZoom() - expect(component.previewZoomSetting).toEqual('1.5') + expect(component.previewZoomSetting()).toEqual('1.5') component.increaseZoom() - expect(component.previewZoomSetting).toEqual('2') + expect(component.previewZoomSetting()).toEqual('2') component.decreaseZoom() - expect(component.previewZoomSetting).toEqual('1.5') + expect(component.previewZoomSetting()).toEqual('1.5') component.setZoom(PdfZoomLevel.One) // from select component.decreaseZoom() - expect(component.previewZoomSetting).toEqual('.75') + expect(component.previewZoomSetting()).toEqual('.75') component.setZoom(PdfZoomScale.PageFit) // from select - expect(component.previewZoomScale).toEqual('page-fit') - expect(component.previewZoomSetting).toEqual('1') + expect(component.previewZoomScale()).toEqual('page-fit') + expect(component.previewZoomSetting()).toEqual('1') component.increaseZoom() - expect(component.previewZoomSetting).toEqual('1.5') - expect(component.previewZoomScale).toEqual('page-width') + expect(component.previewZoomSetting()).toEqual('1.5') + expect(component.previewZoomScale()).toEqual('page-width') component.setZoom(PdfZoomScale.PageFit) // from select - expect(component.previewZoomScale).toEqual('page-fit') - expect(component.previewZoomSetting).toEqual('1') + expect(component.previewZoomScale()).toEqual('page-fit') + expect(component.previewZoomSetting()).toEqual('1') component.decreaseZoom() - expect(component.previewZoomSetting).toEqual('.5') - expect(component.previewZoomScale).toEqual('page-width') + expect(component.previewZoomSetting()).toEqual('.5') + expect(component.previewZoomScale()).toEqual('page-width') }) it('should select correct zoom setting in dropdown', () => { @@ -1088,7 +1091,7 @@ describe('DocumentDetailComponent', () => { initNormally() const refreshSpy = jest.spyOn(openDocumentsService, 'refreshDocument') component.notesUpdated(notes) // called by notes component - expect(component.document.notes).toEqual(notes) + expect(component.document().notes).toEqual(notes) expect(refreshSpy).toHaveBeenCalled() }) @@ -1187,7 +1190,7 @@ describe('DocumentDetailComponent', () => { it('should detect RTL languages and add css class to content textarea', () => { initNormally() - component.metadata = { lang: 'he' } + component.metadata.set({ lang: 'he' }) component.nav.select(2) // content fixture.detectChanges() expect(component.isRTL).toBeTruthy() @@ -1196,7 +1199,7 @@ describe('DocumentDetailComponent', () => { it('should display built-in pdf viewer if not disabled', () => { initNormally() - component.document.archived_file_name = 'file.pdf' + component.document().archived_file_name = 'file.pdf' settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, false) expect(component.useNativePdfViewer).toBeFalsy() fixture.detectChanges() @@ -1205,7 +1208,7 @@ describe('DocumentDetailComponent', () => { it('should display native pdf viewer if enabled', () => { initNormally() - component.document.archived_file_name = 'file.pdf' + component.document().archived_file_name = 'file.pdf' settingsService.set(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER, true) expect(component.useNativePdfViewer).toBeTruthy() fixture.detectChanges() @@ -1223,12 +1226,12 @@ describe('DocumentDetailComponent', () => { const metadataSpy = jest.spyOn(documentService, 'getMetadata') metadataSpy.mockReturnValue(of({ has_archive_version: true })) initNormally() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') expect(metadataSpy).toHaveBeenCalledWith(doc.id, null) metadataSpy.mockClear() - component.document.versions = [ + component.document().versions = [ { id: doc.id, is_root: true }, { id: 10, is_root: false }, ] as any @@ -1267,7 +1270,7 @@ describe('DocumentDetailComponent', () => { expect(component.customFieldFormFields).toHaveLength(initialLength) component.addField(customFields[1]) fixture.detectChanges() - expect(component.document.custom_fields).toHaveLength(initialLength + 1) + expect(component.document().custom_fields).toHaveLength(initialLength + 1) expect(component.customFieldFormFields).toHaveLength(initialLength + 1) expect(fixture.debugElement.nativeElement.textContent).toContain( customFields[1].name @@ -1287,7 +1290,7 @@ describe('DocumentDetailComponent', () => { expect(component.customFieldFormFields).toHaveLength(initialLength) component.removeField(doc.custom_fields[0]) fixture.detectChanges() - expect(component.document.custom_fields).toHaveLength(initialLength - 1) + expect(component.document().custom_fields).toHaveLength(initialLength - 1) expect(component.customFieldFormFields).toHaveLength(initialLength - 1) expect( fixture.debugElement.query(By.css('form ul')).nativeElement.textContent @@ -1355,7 +1358,7 @@ describe('DocumentDetailComponent', () => { it('should show custom field errors', () => { initNormally() - component.error = { + component.error.set({ custom_fields: [ {}, {}, @@ -1363,7 +1366,7 @@ describe('DocumentDetailComponent', () => { {}, { non_field_errors: ['Enter a valid URL.'] }, ], - } + }) expect(component.getCustomFieldError(2)).toEqual([ 'This field may not be null.', ]) @@ -1393,7 +1396,7 @@ describe('DocumentDetailComponent', () => { initNormally() expect(suggestionsSpy).toHaveBeenCalled() expect(aiSuggestionsSpy).not.toHaveBeenCalled() - expect(component.suggestions).toEqual({ + expect(component.suggestions()).toEqual({ tags: [42, 43], suggested_tags: [], suggested_document_types: [], @@ -1421,7 +1424,7 @@ describe('DocumentDetailComponent', () => { initNormally() expect(suggestionsSpy).not.toHaveBeenCalled() expect(aiSuggestionsSpy).toHaveBeenCalled() - expect(component.suggestions).toEqual({ + expect(component.suggestions()).toEqual({ tags: [42, 43], suggested_tags: [], suggested_document_types: [], @@ -1474,7 +1477,7 @@ describe('DocumentDetailComponent', () => { it('should react to websocket document updated notifications', () => { initNormally() const updateMessage = { - document_id: component.documentId, + document_id: component.documentId(), modified: '2026-02-17T00:00:00Z', owner_id: 1, } @@ -1493,18 +1496,18 @@ describe('DocumentDetailComponent', () => { const loadSpy = jest.spyOn(component as any, 'loadDocument') const toastSpy = jest.spyOn(toastService, 'showInfo') - component.networkActive = true + component.networkActive.set(true) ;(component as any).handleIncomingDocumentUpdated({ - document_id: component.documentId, + document_id: component.documentId(), modified: '2026-02-17T00:00:00Z', }) expect(loadSpy).not.toHaveBeenCalled() - component.networkActive = false + component.networkActive.set(false) ;(component as any).flushPendingIncomingUpdate() - expect(loadSpy).toHaveBeenCalledWith(component.documentId, true) + expect(loadSpy).toHaveBeenCalledWith(component.documentId(), true) expect(toastSpy).toHaveBeenCalledWith( 'Document reloaded with latest changes.' ) @@ -1515,14 +1518,14 @@ describe('DocumentDetailComponent', () => { const loadSpy = jest.spyOn(component as any, 'loadDocument') const toastSpy = jest.spyOn(toastService, 'showInfo') - component.networkActive = true + component.networkActive.set(true) ;(component as any).lastLocalSaveModified = '2026-02-17T00:00:00+00:00' ;(component as any).handleIncomingDocumentUpdated({ - document_id: component.documentId, + document_id: component.documentId(), modified: '2026-02-17T00:00:00+00:00', }) - component.networkActive = false + component.networkActive.set(false) ;(component as any).flushPendingIncomingUpdate() expect(loadSpy).not.toHaveBeenCalled() @@ -1530,11 +1533,13 @@ describe('DocumentDetailComponent', () => { }) it('should clear pdf source if preview URL is empty', () => { - component.pdfSource = { url: '/preview', password: 'secret' } as any - component.previewUrl = null + component.pdfSource.set('/preview') + component.pdfPassword.set('secret') + component.previewUrl.set(null) ;(component as any).updatePdfSource() - expect(component.pdfSource).toEqual({ url: null, password: undefined }) + expect(component.pdfSource()).toBeNull() + expect(component.pdfPassword()).toBeUndefined() }) it('should close incoming update modal if one is open', () => { @@ -1604,7 +1609,7 @@ describe('DocumentDetailComponent', () => { const loadSpy = jest.spyOn(component as any, 'loadDocument') ;(component as any).handleIncomingDocumentUpdated({ - document_id: component.documentId + 1, + document_id: component.documentId() + 1, modified: '2026-02-17T00:00:00Z', }) @@ -1619,7 +1624,7 @@ describe('DocumentDetailComponent', () => { .mockImplementation(() => {}) ;(component as any).handleIncomingDocumentUpdated({ - document_id: component.documentId, + document_id: component.documentId(), modified: '2026-02-17T00:00:00Z', }) @@ -1627,7 +1632,7 @@ describe('DocumentDetailComponent', () => { }) it('should reload current document and show toast when reloading remote version', () => { - component.documentId = doc.id + component.documentId.set(doc.id) const closeModalSpy = jest .spyOn(component as any, 'closeIncomingUpdateModal') .mockImplementation(() => {}) @@ -1647,7 +1652,7 @@ describe('DocumentDetailComponent', () => { it('should change preview element by render type', () => { initNormally() - component.document.archived_file_name = 'file.pdf' + component.document().archived_file_name = 'file.pdf' fixture.detectChanges() expect(component.archiveContentRenderType).toEqual( component.ContentRenderType.PDF @@ -1656,8 +1661,8 @@ describe('DocumentDetailComponent', () => { fixture.debugElement.query(By.css('pdf-viewer-container')) ).not.toBeUndefined() - component.document.archived_file_name = undefined - component.document.mime_type = 'text/plain' + component.document().archived_file_name = undefined + component.document().mime_type = 'text/plain' fixture.detectChanges() expect(component.archiveContentRenderType).toEqual( component.ContentRenderType.Text @@ -1666,7 +1671,7 @@ describe('DocumentDetailComponent', () => { fixture.debugElement.query(By.css('div.preview-sticky')) ).not.toBeUndefined() - component.document.mime_type = 'image/jpeg' + component.document().mime_type = 'image/jpeg' fixture.detectChanges() expect(component.archiveContentRenderType).toEqual( component.ContentRenderType.Image @@ -1674,7 +1679,7 @@ describe('DocumentDetailComponent', () => { expect( fixture.debugElement.query(By.css('.preview-sticky img')) ).not.toBeUndefined() - ;((component.document.mime_type = + ;((component.document().mime_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'), fixture.detectChanges()) expect(component.archiveContentRenderType).toEqual( @@ -1691,11 +1696,11 @@ describe('DocumentDetailComponent', () => { const closeSpy = jest.spyOn(openDocumentsService, 'closeDocument') const errorSpy = jest.spyOn(toastService, 'showError') initNormally() - component.selectedVersionId = 10 + component.selectedVersionId.set(10) component.editPdf() expect(modal).not.toBeUndefined() - modal.componentInstance.documentID = doc.id - expect(modal.componentInstance.versionID).toBe(10) + modal.componentInstance.documentID.set(doc.id) + expect(modal.componentInstance.versionID()).toBe(10) modal.componentInstance.pages = [{ page: 1, rotate: 0, splitAfter: false }] modal.componentInstance.confirm() let req = httpTestingController.expectOne( @@ -1713,7 +1718,7 @@ describe('DocumentDetailComponent', () => { expect(errorSpy).toHaveBeenCalled() component.editPdf() - modal.componentInstance.documentID = doc.id + modal.componentInstance.documentID.set(doc.id) modal.componentInstance.pages = [{ page: 1, rotate: 0, splitAfter: true }] modal.componentInstance.deleteOriginal = true modal.componentInstance.confirm() @@ -1728,7 +1733,7 @@ describe('DocumentDetailComponent', () => { let modal: NgbModalRef modalService.activeInstances.subscribe((m) => (modal = m[0])) initNormally() - component.selectedVersionId = 10 + component.selectedVersionId.set(10) component.password = 'secret' component.removePassword() const dialog = @@ -1782,7 +1787,7 @@ describe('DocumentDetailComponent', () => { req.error(new ErrorEvent('failed')) expect(errorSpy).toHaveBeenCalled() - expect(component.networkActive).toBe(false) + expect(component.networkActive()).toBe(false) expect(dialog.buttonsEnabled).toBe(true) }) @@ -1856,7 +1861,7 @@ describe('DocumentDetailComponent', () => { it('selectVersion should update preview and handle preview failures', () => { const previewSpy = jest.spyOn(documentService, 'getPreviewUrl') initNormally() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') previewSpy.mockReturnValueOnce('preview-version') jest.spyOn(documentService, 'getThumbUrl').mockReturnValue('thumb-version') @@ -1867,13 +1872,12 @@ describe('DocumentDetailComponent', () => { component.selectVersion(10) httpTestingController.expectOne('preview-version').flush('version text') - expect(component.previewUrl).toBe('preview-version') - expect(component.thumbUrl).toBe('thumb-version') - expect(component.previewText).toBe('version text') + expect(component.previewUrl()).toBe('preview-version') + expect(component.thumbUrl()).toBe('thumb-version') + expect(component.previewText()).toBe('version text') expect(component.documentForm.get('content').value).toBe('version-content') - const pdfSource = component.pdfSource as { url: string; password?: string } - expect(pdfSource.url).toBe('preview-version') - expect(pdfSource.password).toBeUndefined() + expect(component.pdfSource()).toBe('preview-version') + expect(component.pdfPassword()).toBeUndefined() previewSpy.mockReturnValueOnce('preview-error') component.selectVersion(11) @@ -1881,12 +1885,14 @@ describe('DocumentDetailComponent', () => { .expectOne('preview-error') .error(new ErrorEvent('fail')) - expect(component.previewText).toContain('An error occurred loading content') + expect(component.previewText()).toContain( + 'An error occurred loading content' + ) }) it('selectVersion should show toast if version content retrieval fails', () => { initNormally() - httpTestingController.expectOne(component.previewUrl).flush('preview') + httpTestingController.expectOne(component.previewUrl()).flush('preview') jest.spyOn(documentService, 'getPreviewUrl').mockReturnValue('preview-ok') jest.spyOn(documentService, 'getThumbUrl').mockReturnValue('thumb-ok') @@ -1919,8 +1925,8 @@ describe('DocumentDetailComponent', () => { }) it('onVersionsUpdated should sync open document versions and save', () => { - component.documentId = doc.id - component.document = { ...doc, versions: [] } as Document + component.documentId.set(doc.id) + component.document.set({ ...doc, versions: [] } as Document) const updatedVersions = [ { id: doc.id, is_root: true }, { id: 10, is_root: false }, @@ -1931,7 +1937,7 @@ describe('DocumentDetailComponent', () => { component.onVersionsUpdated(updatedVersions) - expect(component.document.versions).toEqual(updatedVersions) + expect(component.document().versions).toEqual(updatedVersions) expect(openDoc.versions).toEqual(updatedVersions) expect(saveSpy).toHaveBeenCalled() }) @@ -1966,7 +1972,7 @@ describe('DocumentDetailComponent', () => { DocumentDetailComponent.prototype as any, 'tryRenderTiff' ) - const doc = Object.assign({}, component.document) + const doc = Object.assign({}, component.document()) doc.archived_file_name = null doc.mime_type = 'image/tiff' jest @@ -1985,21 +1991,21 @@ describe('DocumentDetailComponent', () => { it('should try to render tiff and show error if failed', () => { initNormally() // just the text request - httpTestingController.expectOne(component.previewUrl) + httpTestingController.expectOne(component.previewUrl()) // invalid tiff component['tryRenderTiff']() httpTestingController - .expectOne(component.previewUrl) + .expectOne(component.previewUrl()) .flush(new ArrayBuffer(100)) // arraybuffer - expect(component.tiffError).not.toBeUndefined() + expect(component.tiffError()).not.toBeUndefined() // http error component['tryRenderTiff']() httpTestingController - .expectOne(component.previewUrl) + .expectOne(component.previewUrl()) .error(new ErrorEvent('failed')) - expect(component.tiffError).not.toBeUndefined() + expect(component.tiffError()).not.toBeUndefined() }) it('should support download using share sheet on mobile, direct download otherwise', () => { @@ -2038,7 +2044,7 @@ describe('DocumentDetailComponent', () => { it('should include version in download and print only for non-latest selected version', () => { initNormally() - component.document.versions = [ + component.document().versions = [ { id: doc.id, is_root: true }, { id: 10, is_root: false }, ] as any @@ -2050,7 +2056,7 @@ describe('DocumentDetailComponent', () => { .mockReturnValueOnce('download-non-latest') .mockReturnValueOnce('print-non-latest') - component.selectedVersionId = 10 + component.selectedVersionId.set(10) component.download() expect(getDownloadUrlSpy).toHaveBeenNthCalledWith( 1, @@ -2069,7 +2075,7 @@ describe('DocumentDetailComponent', () => { .expectOne('print-latest') .error(new ProgressEvent('failed')) - component.selectedVersionId = doc.id + component.selectedVersionId.set(doc.id) component.download() expect(getDownloadUrlSpy).toHaveBeenNthCalledWith( 3, @@ -2091,7 +2097,7 @@ describe('DocumentDetailComponent', () => { it('should omit version in download and print when no version is selected', () => { initNormally() - component.document.versions = [] as any + component.document().versions = [] as any ;(component as any).selectedVersionId = undefined const getDownloadUrlSpy = jest @@ -2128,7 +2134,7 @@ describe('DocumentDetailComponent', () => { }) const downloadUrl = 'http://example.com/download' - component.documentId = 123 + component.documentId.set(123) jest.spyOn(documentService, 'getDownloadUrl').mockReturnValue(downloadUrl) const createSpy = jest.spyOn(document, 'createElement') @@ -2163,19 +2169,22 @@ describe('DocumentDetailComponent', () => { it('should set previewText', () => { initNormally() const previewText = 'Hello world, this is a test' - httpTestingController.expectOne(component.previewUrl).flush(previewText) - expect(component.previewText).toEqual(previewText) + httpTestingController.expectOne(component.previewUrl()).flush(previewText) + expect(component.previewText()).toEqual(previewText) }) it('should set previewText to error message if preview fails', () => { initNormally() httpTestingController - .expectOne(component.previewUrl) + .expectOne(component.previewUrl()) .flush('fail', { status: 500, statusText: 'Server Error' }) - expect(component.previewText).toContain('An error occurred loading content') + expect(component.previewText()).toContain( + 'An error occurred loading content' + ) }) - it('should print document successfully', fakeAsync(() => { + it('should print document successfully', () => { + jest.useFakeTimers() initNormally() const appendChildSpy = jest @@ -2216,8 +2225,6 @@ describe('DocumentDetailComponent', () => { ) req.flush(blob) - tick() - expect(createElementSpy).toHaveBeenCalledWith('iframe') expect(appendChildSpy).toHaveBeenCalledWith(mockIframe) expect(createObjectURLSpy).toHaveBeenCalledWith(blob) @@ -2234,7 +2241,7 @@ describe('DocumentDetailComponent', () => { if (mockIframe.onload) { mockIframe.onload({} as any) } - tick() + jest.advanceTimersByTime(0) expect(mockContentWindow.focus).toHaveBeenCalled() expect(mockContentWindow.print).toHaveBeenCalled() @@ -2247,8 +2254,6 @@ describe('DocumentDetailComponent', () => { mockContentWindow.onafterprint(new Event('afterprint')) } - tick(500) - expect(removeChildSpy).toHaveBeenCalledWith(mockIframe) expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url') @@ -2257,7 +2262,7 @@ describe('DocumentDetailComponent', () => { removeChildSpy.mockRestore() createObjectURLSpy.mockRestore() revokeObjectURLSpy.mockRestore() - })) + }) it('should show error toast if print document fails', () => { initNormally() @@ -2294,76 +2299,71 @@ describe('DocumentDetailComponent', () => { ] iframePrintErrorCases.forEach(({ description, thrownError, expectToast }) => { - it( - description, - fakeAsync(() => { - initNormally() + it(description, () => { + jest.useFakeTimers() + initNormally() - const appendChildSpy = jest - .spyOn(document.body, 'appendChild') - .mockImplementation((node: Node) => node) - const removeChildSpy = jest - .spyOn(document.body, 'removeChild') - .mockImplementation((node: Node) => node) - const createObjectURLSpy = jest - .spyOn(URL, 'createObjectURL') - .mockReturnValue('blob:mock-url') - const revokeObjectURLSpy = jest - .spyOn(URL, 'revokeObjectURL') - .mockImplementation(() => {}) + const appendChildSpy = jest + .spyOn(document.body, 'appendChild') + .mockImplementation((node: Node) => node) + const removeChildSpy = jest + .spyOn(document.body, 'removeChild') + .mockImplementation((node: Node) => node) + const createObjectURLSpy = jest + .spyOn(URL, 'createObjectURL') + .mockReturnValue('blob:mock-url') + const revokeObjectURLSpy = jest + .spyOn(URL, 'revokeObjectURL') + .mockImplementation(() => {}) - const toastSpy = jest.spyOn(toastService, 'showError') + const toastSpy = jest.spyOn(toastService, 'showError') - const mockContentWindow = { - focus: jest.fn().mockImplementation(() => { - throw thrownError - }), - print: jest.fn(), - onafterprint: null, - } + const mockContentWindow = { + focus: jest.fn().mockImplementation(() => { + throw thrownError + }), + print: jest.fn(), + onafterprint: null, + } - const mockIframe: any = { - style: {}, - src: '', - onload: null, - contentWindow: mockContentWindow, - } + const mockIframe: any = { + style: {}, + src: '', + onload: null, + contentWindow: mockContentWindow, + } - const createElementSpy = jest - .spyOn(document, 'createElement') - .mockReturnValue(mockIframe as any) + const createElementSpy = jest + .spyOn(document, 'createElement') + .mockReturnValue(mockIframe as any) - const blob = new Blob(['test'], { type: 'application/pdf' }) - component.printDocument() + const blob = new Blob(['test'], { type: 'application/pdf' }) + component.printDocument() - const req = httpTestingController.expectOne( - `${environment.apiBaseUrl}documents/${doc.id}/download/` - ) - req.flush(blob) + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}documents/${doc.id}/download/` + ) + req.flush(blob) - tick() + if (mockIframe.onload) { + mockIframe.onload(new Event('load')) + } - if (mockIframe.onload) { - mockIframe.onload(new Event('load')) - } + jest.advanceTimersByTime(200) - tick() - tick(200) + if (expectToast) { + expect(toastSpy).toHaveBeenCalled() + } else { + expect(toastSpy).not.toHaveBeenCalled() + } + expect(removeChildSpy).toHaveBeenCalledWith(mockIframe) + expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url') - if (expectToast) { - expect(toastSpy).toHaveBeenCalled() - } else { - expect(toastSpy).not.toHaveBeenCalled() - } - expect(removeChildSpy).toHaveBeenCalledWith(mockIframe) - expect(revokeObjectURLSpy).toHaveBeenCalledWith('blob:mock-url') - - createElementSpy.mockRestore() - appendChildSpy.mockRestore() - removeChildSpy.mockRestore() - createObjectURLSpy.mockRestore() - revokeObjectURLSpy.mockRestore() - }) - ) + createElementSpy.mockRestore() + appendChildSpy.mockRestore() + removeChildSpy.mockRestore() + createObjectURLSpy.mockRestore() + revokeObjectURLSpy.mockRestore() + }) }) }) diff --git a/src-ui/src/app/components/document-detail/document-detail.component.ts b/src-ui/src/app/components/document-detail/document-detail.component.ts index ab6730354..d9d67821c 100644 --- a/src-ui/src/app/components/document-detail/document-detail.component.ts +++ b/src-ui/src/app/components/document-detail/document-detail.component.ts @@ -1,6 +1,13 @@ import { AsyncPipe, NgTemplateOutlet } from '@angular/common' import { HttpClient, HttpResponse } from '@angular/common/http' -import { Component, inject, OnDestroy, OnInit, ViewChild } from '@angular/core' +import { + Component, + inject, + OnDestroy, + OnInit, + signal, + ViewChild, +} from '@angular/core' import { FormArray, FormControl, @@ -115,7 +122,6 @@ import { PDFEditorComponent } from '../common/pdf-editor/pdf-editor.component' import { PngxPdfViewerComponent } from '../common/pdf-viewer/pdf-viewer.component' import { PdfRenderMode, - PdfSource, PdfZoomLevel, PdfZoomScale, PngxPdfDocumentProxy, @@ -228,33 +234,34 @@ export class DocumentDetailComponent expandOriginalMetadata = false expandArchivedMetadata = false - error: any + readonly error = signal(undefined) - networkActive = false + readonly networkActive = signal(false) - documentId: number - document: Document - metadata: DocumentMetadata - suggestions: DocumentSuggestions - suggestionsLoading: boolean = false - users: User[] + readonly documentId = signal(undefined) + readonly document = signal(undefined) + readonly metadata = signal(undefined) + readonly suggestions = signal(undefined) + readonly suggestionsLoading = signal(false) + readonly users = signal(undefined) - title: string + readonly title = signal(undefined) titleSubject: Subject = new Subject() - previewUrl: string - pdfSource?: PdfSource - thumbUrl: string - previewText: string - previewLoaded: boolean = false - tiffURL: string - tiffError: string + readonly previewUrl = signal(undefined) + readonly pdfSource = signal(undefined) + readonly pdfPassword = signal(undefined) + readonly thumbUrl = signal(undefined) + readonly previewText = signal(undefined) + readonly previewLoaded = signal(false) + readonly tiffURL = signal(undefined) + readonly tiffError = signal(undefined) // Versioning - selectedVersionId: number + readonly selectedVersionId = signal(undefined) - correspondents: Correspondent[] - documentTypes: DocumentType[] - storagePaths: StoragePath[] + readonly correspondents = signal(undefined) + readonly documentTypes = signal(undefined) + readonly storagePaths = signal(undefined) documentForm: FormGroup = new FormGroup({ title: new FormControl(''), @@ -269,10 +276,10 @@ export class DocumentDetailComponent custom_fields: new FormArray([]), }) - previewCurrentPage: number = 1 - previewNumPages: number - previewZoomSetting: PdfZoomLevel = PdfZoomLevel.One - previewZoomScale: PdfZoomScale = PdfZoomScale.PageWidth + readonly previewCurrentPage = signal(1) + readonly previewNumPages = signal(undefined) + readonly previewZoomSetting = signal(PdfZoomLevel.One) + readonly previewZoomScale = signal(PdfZoomScale.PageWidth) store: BehaviorSubject isDirty$: Observable @@ -287,9 +294,9 @@ export class DocumentDetailComponent ogDate: Date - customFields: CustomField[] + readonly customFields = signal(undefined) - public downloading: boolean = false + readonly downloading = signal(false) public useFormattedFilename: boolean = false public readonly CustomFieldDataType = CustomFieldDataType @@ -314,13 +321,14 @@ export class DocumentDetailComponent } DocumentDetailNavIDs = DocumentDetailNavIDs - activeNavID: number + readonly activeNavID = signal(undefined) titleKeyUp(event) { this.titleSubject.next(event.target?.value) } get useNativePdfViewer(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.USE_NATIVE_PDF_VIEWER) } @@ -329,30 +337,36 @@ export class DocumentDetailComponent } get aiEnabled(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.AI_ENABLED) } get archiveContentRenderType(): ContentRenderType { + this.settings.trackChanges() const hasArchiveVersion = - this.metadata?.has_archive_version ?? !!this.document?.archived_file_name + this.metadata()?.has_archive_version ?? + !!this.document()?.archived_file_name return hasArchiveVersion ? this.getRenderType('application/pdf') : this.getRenderType( - this.metadata?.original_mime_type || this.document?.mime_type + this.metadata()?.original_mime_type || this.document()?.mime_type ) } get originalContentRenderType(): ContentRenderType { + this.settings.trackChanges() return this.getRenderType( - this.metadata?.original_mime_type || this.document?.mime_type + this.metadata()?.original_mime_type || this.document()?.mime_type ) } get showThumbnailOverlay(): boolean { + this.settings.trackChanges() return this.settings.get(SETTINGS_KEYS.DOCUMENT_EDITING_OVERLAY_THUMBNAIL) } isFieldHidden(fieldId: DocumentDetailFieldID): boolean { + this.settings.trackChanges() return this.settings .get(SETTINGS_KEYS.DOCUMENT_DETAILS_HIDDEN_FIELDS) .includes(fieldId) @@ -375,16 +389,14 @@ export class DocumentDetailComponent } private updatePdfSource() { - this.pdfSource = { - url: this.previewUrl, - password: this.password, - } + this.pdfSource.set(this.previewUrl()) + this.pdfPassword.set(this.password) } private loadMetadataForSelectedVersion() { const selectedVersionId = this.getSelectedNonLatestVersionId() this.documentsService - .getMetadata(this.documentId, selectedVersionId) + .getMetadata(this.documentId(), selectedVersionId) .pipe( first(), takeUntil(this.unsubscribeNotifier), @@ -392,9 +404,9 @@ export class DocumentDetailComponent ) .subscribe({ next: (result) => { - this.metadata = result - this.tiffURL = null - this.tiffError = null + this.metadata.set(result) + this.tiffURL.set(null) + this.tiffError.set(null) if (this.archiveContentRenderType === ContentRenderType.TIFF) { this.tryRenderTiff() } @@ -402,11 +414,11 @@ export class DocumentDetailComponent this.archiveContentRenderType !== ContentRenderType.PDF || this.useNativePdfViewer ) { - this.previewLoaded = true + this.previewLoaded.set(true) } }, error: (error) => { - this.metadata = {} // allow display to fallback to tag + this.metadata.set({}) // allow display to fallback to tag this.toastService.showError( $localize`Error retrieving metadata`, error @@ -416,9 +428,9 @@ export class DocumentDetailComponent } get isRTL() { - if (!this.metadata || !this.metadata.lang) return false + if (!this.metadata()?.lang) return false else { - return ['ar', 'he', 'fe'].includes(this.metadata.lang) + return ['ar', 'he', 'fe'].includes(this.metadata().lang) } } @@ -493,27 +505,31 @@ export class DocumentDetailComponent let redirectedToRoot = false this.closeIncomingUpdateModal() this.pendingIncomingUpdate = null - this.selectedVersionId = documentId + this.selectedVersionId.set(documentId) this.lastLocalSaveModified = null - this.previewUrl = this.documentsService.getPreviewUrl( - this.selectedVersionId + this.previewUrl.set( + this.documentsService.getPreviewUrl(this.selectedVersionId()) ) this.updatePdfSource() this.http - .get(this.previewUrl, { responseType: 'text' }) + .get(this.previewUrl(), { responseType: 'text' }) .pipe( first(), takeUntil(this.unsubscribeNotifier), takeUntil(this.docChangeNotifier) ) .subscribe({ - next: (res) => (this.previewText = res.toString()), + next: (res) => this.previewText.set(res.toString()), error: (err) => - (this.previewText = $localize`An error occurred loading content: ${ - err.message ?? err.toString() - }`), + this.previewText.set( + $localize`An error occurred loading content: ${ + err.message ?? err.toString() + }` + ), }) - this.thumbUrl = this.documentsService.getThumbUrl(this.selectedVersionId) + this.thumbUrl.set( + this.documentsService.getThumbUrl(this.selectedVersionId()) + ) this.documentsService .get(documentId) .pipe( @@ -552,10 +568,10 @@ export class DocumentDetailComponent this.router.navigate(['404'], { replaceUrl: true }) return } - this.documentId = doc.id - this.suggestions = null + this.documentId.set(doc.id) + this.suggestions.set(null) const openDocument = this.openDocumentService.getOpenDocument( - this.documentId + this.documentId() ) // update duplicate documents if present if (openDocument && doc?.duplicate_documents) { @@ -602,7 +618,7 @@ export class DocumentDetailComponent ) .subscribe((titleValue) => { if (titleValue !== this.titleInput.value) return - this.title = titleValue + this.title.set(titleValue) this.documentForm.patchValue({ title: titleValue }) this.documentForm.get('title').markAsDirty() }) @@ -653,7 +669,7 @@ export class DocumentDetailComponent } private flushPendingIncomingUpdate() { - if (!this.pendingIncomingUpdate || this.networkActive) return + if (!this.pendingIncomingUpdate || this.networkActive()) return const pendingUpdate = this.pendingIncomingUpdate this.pendingIncomingUpdate = null this.handleIncomingDocumentUpdated(pendingUpdate) @@ -661,12 +677,12 @@ export class DocumentDetailComponent private handleIncomingDocumentUpdated(data: IncomingDocumentUpdate): void { if ( - !this.documentId || - !this.document || - data.document_id !== this.documentId + !this.documentId() || + !this.document() || + data.document_id !== this.documentId() ) return - if (this.networkActive) { + if (this.networkActive()) { this.pendingIncomingUpdate = data return } @@ -683,11 +699,11 @@ export class DocumentDetailComponent } this.lastLocalSaveModified = null - if (this.openDocumentService.isDirty(this.document)) { + if (this.openDocumentService.isDirty(this.document())) { this.showIncomingUpdateModal(data.modified) } else { - this.docChangeNotifier.next(this.documentId) - this.loadDocument(this.documentId, true) + this.docChangeNotifier.next(this.documentId()) + this.loadDocument(this.documentId(), true) this.toastService.showInfo( $localize`Document reloaded with latest changes.` ) @@ -695,11 +711,11 @@ export class DocumentDetailComponent } private reloadRemoteVersion() { - if (!this.documentId) return + if (!this.documentId()) return this.closeIncomingUpdateModal() - this.docChangeNotifier.next(this.documentId) - this.loadDocument(this.documentId, true) + this.docChangeNotifier.next(this.documentId()) + this.loadDocument(this.documentId(), true) this.toastService.showInfo($localize`Document reloaded.`) } @@ -710,8 +726,8 @@ export class DocumentDetailComponent this.documentForm.valueChanges .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe((values) => { - this.error = null - Object.assign(this.document, this.mapFormToDoc(values)) + this.error.set(null) + Object.assign(this.document(), this.mapFormToDoc(values)) }) if ( @@ -723,7 +739,7 @@ export class DocumentDetailComponent this.correspondentService .listAll() .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.correspondents = result.results)) + .subscribe((result) => this.correspondents.set(result.results)) } if ( this.permissionsService.currentUserCan( @@ -734,7 +750,7 @@ export class DocumentDetailComponent this.documentTypeService .listAll() .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.documentTypes = result.results)) + .subscribe((result) => this.documentTypes.set(result.results)) } if ( this.permissionsService.currentUserCan( @@ -745,7 +761,7 @@ export class DocumentDetailComponent this.storagePathService .listAll() .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.storagePaths = result.results)) + .subscribe((result) => this.storagePaths.set(result.results)) } if ( this.permissionsService.currentUserCan( @@ -756,7 +772,7 @@ export class DocumentDetailComponent this.userService .listAll() .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.users = result.results)) + .subscribe((result) => this.users.set(result.results)) } this.getCustomFields() @@ -770,7 +786,7 @@ export class DocumentDetailComponent .pipe( filter( (paramMap) => - +paramMap.get('id') !== this.documentId && + +paramMap.get('id') !== this.documentId() && paramMap.get('section')?.length > 0 ), takeUntil(this.unsubscribeNotifier) @@ -790,7 +806,7 @@ export class DocumentDetailComponent (navID) => navID.toLowerCase() == section ) if (navIDKey) { - this.activeNavID = DocumentDetailNavIDs[navIDKey] + this.activeNavID.set(DocumentDetailNavIDs[navIDKey]) } } else if (paramMap.get('id')) { this.router.navigate(['documents', +paramMap.get('id'), 'details'], { @@ -830,7 +846,7 @@ export class DocumentDetailComponent .addShortcut({ keys: 'control.s', description: $localize`Save document` }) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - if (this.openDocumentService.isDirty(this.document)) this.save() + if (this.openDocumentService.isDirty(this.document())) this.save() }) this.hotKeyService @@ -840,7 +856,7 @@ export class DocumentDetailComponent }) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - if (this.openDocumentService.isDirty(this.document)) { + if (this.openDocumentService.isDirty(this.document())) { if (this.hasNext()) this.saveEditNext() else this.save(true) } @@ -859,19 +875,21 @@ export class DocumentDetailComponent if (foundNavIDkey) this.router.navigate([ 'documents', - this.documentId, + this.documentId(), foundNavIDkey.toLowerCase(), ]) } updateComponent(doc: Document) { - this.document = doc + this.document.set(doc) // Default selected version is the newest version const versions = doc.versions ?? [] - this.selectedVersionId = versions.length - ? Math.max(...versions.map((version) => version.id)) - : doc.id - this.previewLoaded = false + this.selectedVersionId.set( + versions.length + ? Math.max(...versions.map((version) => version.id)) + : doc.id + ) + this.previewLoaded.set(false) this.requiresPassword = false this.updateFormForCustomFields() this.loadMetadataForSelectedVersion() @@ -892,34 +910,38 @@ export class DocumentDetailComponent } }) } - this.title = this.documentTitlePipe.transform(doc.title) + this.title.set(this.documentTitlePipe.transform(doc.title)) this.prepareForm(doc) if ( - this.activeNavID === DocumentDetailNavIDs.Duplicates && + this.activeNavID() === DocumentDetailNavIDs.Duplicates && !doc?.duplicate_documents?.length ) { - this.activeNavID = DocumentDetailNavIDs.Details + this.activeNavID.set(DocumentDetailNavIDs.Details) } } // Update file preview and download target to a specific version (by document id) selectVersion(versionId: number) { - this.selectedVersionId = versionId - this.previewLoaded = false - this.previewUrl = this.documentsService.getPreviewUrl( - this.documentId, - false, - this.selectedVersionId + this.selectedVersionId.set(versionId) + this.previewLoaded.set(false) + this.previewUrl.set( + this.documentsService.getPreviewUrl( + this.documentId(), + false, + this.selectedVersionId() + ) ) this.updatePdfSource() - this.thumbUrl = this.documentsService.getThumbUrl( - this.documentId, - this.selectedVersionId + this.thumbUrl.set( + this.documentsService.getThumbUrl( + this.documentId(), + this.selectedVersionId() + ) ) this.loadMetadataForSelectedVersion() this.documentsService - .get(this.documentId, this.selectedVersionId, 'content') + .get(this.documentId(), this.selectedVersionId(), 'content') .pipe( first(), takeUntil(this.unsubscribeNotifier), @@ -928,7 +950,7 @@ export class DocumentDetailComponent .subscribe({ next: (doc) => { const content = doc?.content ?? '' - this.document.content = content + this.document().content = content this.documentForm.patchValue( { content, @@ -947,18 +969,20 @@ export class DocumentDetailComponent }) // For text previews, refresh content this.http - .get(this.previewUrl, { responseType: 'text' }) + .get(this.previewUrl(), { responseType: 'text' }) .pipe( first(), takeUntil(this.unsubscribeNotifier), takeUntil(this.docChangeNotifier) ) .subscribe({ - next: (res) => (this.previewText = res.toString()), + next: (res) => this.previewText.set(res.toString()), error: (err) => - (this.previewText = $localize`An error occurred loading content: ${ - err.message ?? err.toString() - }`), + this.previewText.set( + $localize`An error occurred loading content: ${ + err.message ?? err.toString() + }` + ), }) } @@ -967,8 +991,8 @@ export class DocumentDetailComponent } onVersionsUpdated(versions: DocumentVersionInfo[]) { - this.document.versions = versions - const openDoc = this.openDocumentService.getOpenDocument(this.documentId) + this.document().versions = versions + const openDoc = this.openDocumentService.getOpenDocument(this.documentId()) if (openDoc) { openDoc.versions = versions this.openDocumentService.save() @@ -980,10 +1004,10 @@ export class DocumentDetailComponent } getSuggestions() { - this.suggestionsLoading = true + this.suggestionsLoading.set(true) const suggestionsObservable = this.aiEnabled - ? this.documentsService.getAiSuggestions(this.documentId) - : this.documentsService.getSuggestions(this.documentId) + ? this.documentsService.getAiSuggestions(this.documentId()) + : this.documentsService.getSuggestions(this.documentId()) suggestionsObservable .pipe( first(), @@ -992,12 +1016,12 @@ export class DocumentDetailComponent ) .subscribe({ next: (result) => { - this.suggestions = result - this.suggestionsLoading = false + this.suggestions.set(result) + this.suggestionsLoading.set(false) }, error: (error) => { - this.suggestions = null - this.suggestionsLoading = false + this.suggestions.set(null) + this.suggestionsLoading.set(false) this.toastService.showError( $localize`Error retrieving suggestions.`, error @@ -1010,19 +1034,19 @@ export class DocumentDetailComponent var modal = this.modalService.open(TagEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) if (newName) modal.componentInstance.object = { name: newName } modal.componentInstance.succeeded .pipe( tap((newTag: Tag) => { // remove from suggestions if present - if (this.suggestions) { - this.suggestions = { - ...this.suggestions, - suggested_tags: this.suggestions.suggested_tags.filter( + if (this.suggestions()) { + this.suggestions.set({ + ...this.suggestions(), + suggested_tags: this.suggestions().suggested_tags.filter( (tag) => tag !== newTag.name ), - } + }) } }), switchMap((newTag: Tag) => { @@ -1042,7 +1066,7 @@ export class DocumentDetailComponent var modal = this.modalService.open(DocumentTypeEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) if (newName) modal.componentInstance.object = { name: newName } modal.componentInstance.succeeded .pipe( @@ -1054,12 +1078,12 @@ export class DocumentDetailComponent ) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(({ newDocumentType, documentTypes }) => { - this.documentTypes = documentTypes.results + this.documentTypes.set(documentTypes.results) this.documentForm.get('document_type').setValue(newDocumentType.id) this.documentForm.get('document_type').markAsDirty() - if (this.suggestions) { - this.suggestions.suggested_document_types = - this.suggestions.suggested_document_types.filter( + if (this.suggestions()) { + this.suggestions().suggested_document_types = + this.suggestions().suggested_document_types.filter( (dt) => dt !== newName ) } @@ -1070,7 +1094,7 @@ export class DocumentDetailComponent var modal = this.modalService.open(CorrespondentEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) if (newName) modal.componentInstance.object = { name: newName } modal.componentInstance.succeeded .pipe( @@ -1084,12 +1108,12 @@ export class DocumentDetailComponent ) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(({ newCorrespondent, correspondents }) => { - this.correspondents = correspondents.results + this.correspondents.set(correspondents.results) this.documentForm.get('correspondent').setValue(newCorrespondent.id) this.documentForm.get('correspondent').markAsDirty() - if (this.suggestions) { - this.suggestions.suggested_correspondents = - this.suggestions.suggested_correspondents.filter( + if (this.suggestions()) { + this.suggestions().suggested_correspondents = + this.suggestions().suggested_correspondents.filter( (c) => c !== newName ) } @@ -1100,7 +1124,7 @@ export class DocumentDetailComponent var modal = this.modalService.open(StoragePathEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) if (newName) modal.componentInstance.object = { name: newName } modal.componentInstance.succeeded .pipe( @@ -1112,7 +1136,7 @@ export class DocumentDetailComponent ) .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(({ newStoragePath, storagePaths }) => { - this.storagePaths = storagePaths.results + this.storagePaths.set(storagePaths.results) this.documentForm.get('storage_path').setValue(newStoragePath.id) this.documentForm.get('storage_path').markAsDirty() }) @@ -1145,7 +1169,7 @@ export class DocumentDetailComponent discard() { this.documentsService - .get(this.documentId, this.selectedVersionId) + .get(this.documentId(), this.selectedVersionId()) .pipe( first(), takeUntil(this.unsubscribeNotifier), @@ -1154,12 +1178,12 @@ export class DocumentDetailComponent .subscribe({ next: (doc) => { this.closeIncomingUpdateModal() - Object.assign(this.document, doc) + Object.assign(this.document(), doc) doc['permissions_form'] = { owner: doc.owner, set_permissions: doc.permissions, } - this.title = doc.title + this.title.set(doc.title) this.updateFormForCustomFields() this.documentForm.patchValue(doc) this.documentForm.markAsPristine() @@ -1175,7 +1199,7 @@ export class DocumentDetailComponent private getChangedFields(): any { const changes = { - id: this.document.id, + id: this.document().id, } Object.keys(this.documentForm.controls).forEach((key) => { if (this.documentForm.get(key).dirty) { @@ -1193,10 +1217,10 @@ export class DocumentDetailComponent } save(close: boolean = false) { - this.networkActive = true + this.networkActive.set(true) ;(document.activeElement as HTMLElement)?.dispatchEvent(new Event('change')) this.documentsService - .patch(this.getChangedFields(), this.selectedVersionId) + .patch(this.getChangedFields(), this.selectedVersionId()) .pipe(first()) .subscribe({ next: (docValues) => { @@ -1208,43 +1232,43 @@ export class DocumentDetailComponent newValues.tags = [...docValues.tags] newValues.custom_fields = [...docValues.custom_fields] this.store.next(newValues) - this.openDocumentService.setDirty(this.document, false) + this.openDocumentService.setDirty(this.document(), false) this.openDocumentService.save() this.toastService.showInfo( $localize`Document "${newValues.title}" saved successfully.` ) - this.networkActive = false - this.error = null + this.networkActive.set(false) + this.error.set(null) if (close) { this.pendingIncomingUpdate = null this.close(() => - this.openDocumentService.refreshDocument(this.documentId) + this.openDocumentService.refreshDocument(this.documentId()) ) } else { - this.openDocumentService.refreshDocument(this.documentId) + this.openDocumentService.refreshDocument(this.documentId()) this.flushPendingIncomingUpdate() } this.savedViewService.maybeRefreshDocumentCounts() }, error: (error) => { - this.networkActive = false + this.networkActive.set(false) this.lastLocalSaveModified = null const canEdit = this.permissionsService.currentUserHasObjectPermissions( PermissionAction.Change, - this.document + this.document() ) if (!canEdit) { // document was 'given away' - this.openDocumentService.setDirty(this.document, false) + this.openDocumentService.setDirty(this.document(), false) this.toastService.showInfo( - $localize`Document "${this.document.title}" saved successfully.` + $localize`Document "${this.document().title}" saved successfully.` ) this.close() } else { - this.error = error.error + this.error.set(error.error) this.toastService.showError( - $localize`Error saving document "${this.document.title}"`, + $localize`Error saving document "${this.document().title}"`, error ) } @@ -1254,14 +1278,14 @@ export class DocumentDetailComponent } saveEditNext() { - this.networkActive = true + this.networkActive.set(true) this.store.next(this.documentForm.value) this.documentsService - .patch(this.getChangedFields(), this.selectedVersionId) + .patch(this.getChangedFields(), this.selectedVersionId()) .pipe( switchMap((updateResult) => { this.savedViewService.maybeRefreshDocumentCounts() - return this.documentListViewService.getNext(this.documentId).pipe( + return this.documentListViewService.getNext(this.documentId()).pipe( map((nextDocId) => ({ nextDocId, updateResult })), takeUntil(this.unsubscribeNotifier) ) @@ -1270,9 +1294,9 @@ export class DocumentDetailComponent .pipe( switchMap(({ nextDocId, updateResult }) => { if (nextDocId && updateResult) { - this.openDocumentService.setDirty(this.document, false) + this.openDocumentService.setDirty(this.document(), false) return this.openDocumentService - .closeDocument(this.document) + .closeDocument(this.document()) .pipe( map( (closeResult) => ({ updateResult, nextDocId, closeResult }), @@ -1286,8 +1310,8 @@ export class DocumentDetailComponent .subscribe({ next: ({ updateResult, nextDocId, closeResult }) => { this.closeIncomingUpdateModal() - this.error = null - this.networkActive = false + this.error.set(null) + this.networkActive.set(false) this.pendingIncomingUpdate = null this.lastLocalSaveModified = null if (closeResult && updateResult && nextDocId) { @@ -1296,9 +1320,9 @@ export class DocumentDetailComponent } }, error: (error) => { - this.networkActive = false + this.networkActive.set(false) this.lastLocalSaveModified = null - this.error = error.error + this.error.set(error.error) this.toastService.showError($localize`Error saving document`, error) this.flushPendingIncomingUpdate() }, @@ -1307,7 +1331,7 @@ export class DocumentDetailComponent close(closedCallback: () => void = null) { this.openDocumentService - .closeDocument(this.document) + .closeDocument(this.document()) .pipe(first()) .subscribe((closed) => { if (!closed) return @@ -1332,7 +1356,7 @@ export class DocumentDetailComponent backdrop: 'static', }) modal.componentInstance.title = $localize`Confirm` - modal.componentInstance.messageBold = $localize`Do you really want to move the document "${this.document.title}" to the trash?` + modal.componentInstance.messageBold = $localize`Do you really want to move the document "${this.document().title}" to the trash?` modal.componentInstance.message = $localize`Documents can be restored prior to permanent deletion.` modal.componentInstance.btnClass = 'btn-danger' modal.componentInstance.btnCaption = $localize`Move to trash` @@ -1344,7 +1368,7 @@ export class DocumentDetailComponent .pipe( switchMap(() => { modal.componentInstance.buttonsEnabled = false - return this.documentsService.delete(this.document) + return this.documentsService.delete(this.document()) }) ) .pipe(takeUntil(this.unsubscribeNotifier)) @@ -1365,7 +1389,7 @@ export class DocumentDetailComponent this.documentListViewService.quickFilter([ { rule_type: FILTER_FULLTEXT_MORELIKE, - value: this.documentId.toString(), + value: this.documentId().toString(), }, ]) } @@ -1382,11 +1406,11 @@ export class DocumentDetailComponent modal.componentInstance.confirmClicked.subscribe(() => { modal.componentInstance.buttonsEnabled = false this.documentsService - .reprocessDocuments({ documents: [this.document.id] }) + .reprocessDocuments({ documents: [this.document().id] }) .subscribe({ next: () => { this.toastService.showInfo( - $localize`Reprocess operation for "${this.document.title}" will begin in the background.` + $localize`Reprocess operation for "${this.document().title}" will begin in the background.` ) if (modal) { modal.close() @@ -1448,21 +1472,21 @@ export class DocumentDetailComponent } private getSelectedNonLatestVersionId(): number | null { - const versions = this.document?.versions ?? [] - if (!versions.length || !this.selectedVersionId) { + const versions = this.document()?.versions ?? [] + if (!versions.length || !this.selectedVersionId()) { return null } const latestVersionId = Math.max(...versions.map((version) => version.id)) - return this.selectedVersionId === latestVersionId + return this.selectedVersionId() === latestVersionId ? null - : this.selectedVersionId + : this.selectedVersionId() } download(original: boolean = false) { - this.downloading = true + this.downloading.set(true) const selectedVersionId = this.getSelectedNonLatestVersionId() const downloadUrl = this.documentsService.getDownloadUrl( - this.documentId, + this.documentId(), original, selectedVersionId, this.useFormattedFilename @@ -1474,11 +1498,11 @@ export class DocumentDetailComponent const contentDisposition = response.headers.get('Content-Disposition') const filename = getFilenameFromContentDisposition(contentDisposition) || - this.document.title + this.document().title const blob = new Blob([response.body], { type: response.body.type, }) - this.downloading = false + this.downloading.set(false) const file = new File([blob], filename, { type: response.body.type, }) @@ -1500,7 +1524,7 @@ export class DocumentDetailComponent } }, error: (error) => { - this.downloading = false + this.downloading.set(false) this.toastService.showError( $localize`Error downloading document`, error @@ -1510,16 +1534,16 @@ export class DocumentDetailComponent } hasNext() { - return this.documentListViewService.hasNext(this.documentId) + return this.documentListViewService.hasNext(this.documentId()) } hasPrevious() { - return this.documentListViewService.hasPrevious(this.documentId) + return this.documentListViewService.hasPrevious(this.documentId()) } nextDoc() { this.documentListViewService - .getNext(this.document.id) + .getNext(this.document().id) .subscribe((nextDocId: number) => { this.router.navigate(['documents', nextDocId]) }) @@ -1527,24 +1551,22 @@ export class DocumentDetailComponent previousDoc() { this.documentListViewService - .getPrevious(this.document.id) + .getPrevious(this.document().id) .subscribe((prevDocId: number) => { this.router.navigate(['documents', prevDocId]) }) } pdfPreviewLoaded(pdf: PngxPdfDocumentProxy) { - this.previewNumPages = pdf.numPages + this.previewNumPages.set(pdf.numPages) if (this.password) this.requiresPassword = false - setTimeout(() => { - this.previewLoaded = true - }, 300) + this.previewLoaded.set(true) } onError(event) { if (event.name == 'PasswordException') { this.requiresPassword = true - this.previewLoaded = true + this.previewLoaded.set(true) } } @@ -1560,12 +1582,12 @@ export class DocumentDetailComponent setting === PdfZoomScale.PageFit || setting === PdfZoomScale.PageWidth ) { - this.previewZoomScale = setting - this.previewZoomSetting = PdfZoomLevel.One + this.previewZoomScale.set(setting) + this.previewZoomSetting.set(PdfZoomLevel.One) return } - this.previewZoomSetting = setting - this.previewZoomScale = PdfZoomScale.PageWidth + this.previewZoomSetting.set(setting) + this.previewZoomScale.set(PdfZoomScale.PageWidth) } get zoomSettings() { @@ -1573,10 +1595,10 @@ export class DocumentDetailComponent } get currentZoom() { - if (this.previewZoomScale === PdfZoomScale.PageFit) { + if (this.previewZoomScale() === PdfZoomScale.PageFit) { return PdfZoomScale.PageFit } - return this.previewZoomSetting + return this.previewZoomSetting() } getZoomSettingTitle(setting: PdfZoomScale | PdfZoomLevel): string { @@ -1590,23 +1612,24 @@ export class DocumentDetailComponent increaseZoom(): void { const zoomLevels = Object.values(PdfZoomLevel) - let currentIndex = zoomLevels.indexOf(this.previewZoomSetting) - if (this.previewZoomScale === PdfZoomScale.PageFit) { + let currentIndex = zoomLevels.indexOf(this.previewZoomSetting()) + if (this.previewZoomScale() === PdfZoomScale.PageFit) { currentIndex = zoomLevels.indexOf(PdfZoomLevel.One) } - this.previewZoomScale = PdfZoomScale.PageWidth - this.previewZoomSetting = + this.previewZoomScale.set(PdfZoomScale.PageWidth) + this.previewZoomSetting.set( zoomLevels[Math.min(zoomLevels.length - 1, currentIndex + 1)] + ) } decreaseZoom(): void { const zoomLevels = Object.values(PdfZoomLevel) - let currentIndex = zoomLevels.indexOf(this.previewZoomSetting) - if (this.previewZoomScale === PdfZoomScale.PageFit) { + let currentIndex = zoomLevels.indexOf(this.previewZoomSetting()) + if (this.previewZoomScale() === PdfZoomScale.PageFit) { currentIndex = zoomLevels.indexOf(PdfZoomLevel.ThreeQuarters) } - this.previewZoomScale = PdfZoomScale.PageWidth - this.previewZoomSetting = zoomLevels[Math.max(0, currentIndex - 1)] + this.previewZoomScale.set(PdfZoomScale.PageWidth) + this.previewZoomSetting.set(zoomLevels[Math.max(0, currentIndex - 1)]) } get showPermissions(): boolean { @@ -1640,33 +1663,35 @@ export class DocumentDetailComponent } notesUpdated(notes: DocumentNote[]) { - this.document.notes = notes - this.openDocumentService.refreshDocument(this.documentId) + this.document().notes = notes + this.openDocumentService.refreshDocument(this.documentId()) this.savedViewService.maybeRefreshDocumentCounts() } get userIsOwner(): boolean { - let doc: Document = Object.assign({}, this.document) + let doc: Document = { ...this.document() } // dont disable while editing if ( - this.document && + this.document() && this.store?.value.permissions_form?.hasOwnProperty('owner') ) { doc.owner = this.store.value.permissions_form.owner } - return !this.document || this.permissionsService.currentUserOwnsObject(doc) + return ( + !this.document() || this.permissionsService.currentUserOwnsObject(doc) + ) } get userCanEdit(): boolean { - let doc: Document = Object.assign({}, this.document) + let doc: Document = { ...this.document() } // dont disable while editing if ( - this.document && + this.document() && this.store?.value.permissions_form?.hasOwnProperty('owner') ) { doc.owner = this.store.value.permissions_form.owner } - return !this.document || this.userCanEditDoc(doc) + return !this.document() || this.userCanEditDoc(doc) } private userCanEditDoc(doc: Document): boolean { @@ -1740,7 +1765,7 @@ export class DocumentDetailComponent this.customFieldsService .listAll() .pipe(first(), takeUntil(this.unsubscribeNotifier)) - .subscribe((result) => (this.customFields = result.results)) + .subscribe((result) => this.customFields.set(result.results)) } public refreshCustomFields() { @@ -1751,17 +1776,17 @@ export class DocumentDetailComponent public getCustomFieldFromInstance( instance: CustomFieldInstance ): CustomField { - return this.customFields?.find((f) => f.id === instance.field) + return this.customFields()?.find((f) => f.id === instance.field) } public getCustomFieldError(index: number) { - const fieldError = this.error?.custom_fields?.[index] + const fieldError = this.error()?.custom_fields?.[index] return fieldError?.['non_field_errors'] ?? fieldError?.['value'] } private updateFormForCustomFields(emitEvent: boolean = false) { this.customFieldFormFields.clear({ emitEvent: false }) - this.document.custom_fields?.forEach((fieldInstance) => { + this.document().custom_fields?.forEach((fieldInstance) => { this.customFieldFormFields.push( new FormGroup({ field: new FormControl(fieldInstance.field), @@ -1773,10 +1798,10 @@ export class DocumentDetailComponent } public addField(field: CustomField) { - this.document.custom_fields.push({ + this.document().custom_fields.push({ field: field.id, value: null, - document: this.documentId, + document: this.documentId(), created: new Date(), }) this.updateFormForCustomFields(true) @@ -1785,8 +1810,8 @@ export class DocumentDetailComponent } public removeField(fieldInstance: CustomFieldInstance) { - this.document.custom_fields.splice( - this.document.custom_fields.indexOf(fieldInstance), + this.document().custom_fields.splice( + this.document().custom_fields.indexOf(fieldInstance), 1 ) this.updateFormForCustomFields(true) @@ -1800,11 +1825,11 @@ export class DocumentDetailComponent size: 'xl', scrollable: true, }) - const sourceDocumentId = this.selectedVersionId ?? this.document.id + const sourceDocumentId = this.selectedVersionId() ?? this.document().id modal.componentInstance.title = $localize`PDF Editor` modal.componentInstance.btnCaption = $localize`Proceed` - modal.componentInstance.documentID = this.document.id - modal.componentInstance.versionID = sourceDocumentId + modal.componentInstance.documentID.set(this.document().id) + modal.componentInstance.versionID.set(sourceDocumentId) modal.componentInstance.confirmClicked .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { @@ -1822,11 +1847,11 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.showInfo( - $localize`PDF edit operation for "${this.document.title}" will begin in the background.` + $localize`PDF edit operation for "${this.document().title}" will begin in the background.` ) modal.close() if (modal.componentInstance.deleteOriginal) { - this.openDocumentService.closeDocument(this.document) + this.openDocumentService.closeDocument(this.document()) } }, error: (error) => { @@ -1862,11 +1887,11 @@ export class DocumentDetailComponent modal.componentInstance.confirmClicked .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - const sourceDocumentId = this.selectedVersionId ?? this.document.id + const sourceDocumentId = this.selectedVersionId() ?? this.document().id const dialog = modal.componentInstance as PasswordRemovalConfirmDialogComponent dialog.buttonsEnabled = false - this.networkActive = true + this.networkActive.set(true) this.documentsService .removePasswordDocuments([sourceDocumentId], { password: this.password, @@ -1879,19 +1904,19 @@ export class DocumentDetailComponent .subscribe({ next: () => { this.toastService.showInfo( - $localize`Password removal operation for "${this.document.title}" will begin in the background.` + $localize`Password removal operation for "${this.document().title}" will begin in the background.` ) - this.networkActive = false + this.networkActive.set(false) modal.close() if (!dialog.updateDocument && dialog.deleteOriginal) { - this.openDocumentService.closeDocument(this.document) + this.openDocumentService.closeDocument(this.document()) } else if (dialog.updateDocument) { - this.openDocumentService.refreshDocument(this.documentId) + this.openDocumentService.refreshDocument(this.documentId()) } }, error: (error) => { dialog.buttonsEnabled = true - this.networkActive = false + this.networkActive.set(false) this.toastService.showError( $localize`Error executing password removal operation`, error @@ -1904,7 +1929,7 @@ export class DocumentDetailComponent printDocument() { const selectedVersionId = this.getSelectedNonLatestVersionId() const printUrl = this.documentsService.getDownloadUrl( - this.document.id, + this.document().id, false, selectedVersionId ) @@ -1960,9 +1985,11 @@ export class DocumentDetailComponent public openShareLinks() { const modal = this.modalService.open(ShareLinksDialogComponent) - modal.componentInstance.documentId = this.document.id - modal.componentInstance.hasArchiveVersion = - this.metadata?.has_archive_version ?? !!this.document?.archived_file_name + modal.componentInstance.documentId.set(this.document().id) + modal.componentInstance.hasArchiveVersion.set( + this.metadata()?.has_archive_version ?? + !!this.document()?.archived_file_name + ) } get emailEnabled(): boolean { @@ -1973,14 +2000,16 @@ export class DocumentDetailComponent const modal = this.modalService.open(EmailDocumentDialogComponent, { backdrop: 'static', }) - modal.componentInstance.documentIds = [this.document.id] - modal.componentInstance.hasArchiveVersion = - this.metadata?.has_archive_version ?? !!this.document?.archived_file_name + modal.componentInstance.documentIds.set([this.document().id]) + modal.componentInstance.hasArchiveVersion.set( + this.metadata()?.has_archive_version ?? + !!this.document()?.archived_file_name + ) } private tryRenderTiff() { this.http - .get(this.previewUrl, { responseType: 'arraybuffer' }) + .get(this.previewUrl(), { responseType: 'arraybuffer' }) .pipe( first(), takeUntil(this.unsubscribeNotifier), @@ -2015,13 +2044,17 @@ export class DocumentDetailComponent imgd = ctx.createImageData(w, h) for (var i = 0; i < rgba.length; i++) imgd.data[i] = rgba[i] ctx.putImageData(imgd, 0, 0) - this.tiffURL = cnv.toDataURL() + this.tiffURL.set(cnv.toDataURL()) } catch (err) { - this.tiffError = $localize`An error occurred loading tiff: ${err.toString()}` + this.tiffError.set( + $localize`An error occurred loading tiff: ${err.toString()}` + ) } }, error: (err) => { - this.tiffError = $localize`An error occurred loading tiff: ${err.toString()}` + this.tiffError.set( + $localize`An error occurred loading tiff: ${err.toString()}` + ) }, }) } diff --git a/src-ui/src/app/components/document-detail/document-history/document-history.component.html b/src-ui/src/app/components/document-detail/document-history/document-history.component.html index 01447ef03..0818efadf 100644 --- a/src-ui/src/app/components/document-detail/document-history/document-history.component.html +++ b/src-ui/src/app/components/document-detail/document-history/document-history.component.html @@ -1,17 +1,17 @@ -@if (loading) { +@if (loading()) {
            } @else {
              - @if (entries.length === 0) { + @if (entries().length === 0) {
            • No entries found.
            • } @else { - @for (entry of entries; track entry.id) { + @for (entry of entries(); track entry.id) {
            • diff --git a/src-ui/src/app/components/document-detail/document-history/document-history.component.ts b/src-ui/src/app/components/document-detail/document-history/document-history.component.ts index a2bbede3c..b4a21270c 100644 --- a/src-ui/src/app/components/document-detail/document-history/document-history.component.ts +++ b/src-ui/src/app/components/document-detail/document-history/document-history.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe, KeyValuePipe, TitleCasePipe } from '@angular/common' -import { Component, Input, OnInit, inject } from '@angular/core' +import { Component, Input, OnInit, inject, signal } from '@angular/core' import { NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' import { Observable, first, map, of, shareReplay } from 'rxjs' @@ -44,8 +44,8 @@ export class DocumentHistoryComponent implements OnInit { } } - public loading: boolean = true - public entries: AuditLogEntry[] = [] + readonly loading = signal(true) + readonly entries = signal([]) private readonly prettyNameCache = new Map>() @@ -55,10 +55,10 @@ export class DocumentHistoryComponent implements OnInit { private loadHistory(): void { if (this._documentId) { - this.loading = true + this.loading.set(true) this.documentService.getHistory(this._documentId).subscribe((entries) => { - this.entries = entries - this.loading = false + this.entries.set(entries) + this.loading.set(false) }) } } diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts index 8f82be1ab..07cdabc9a 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.spec.ts @@ -4,7 +4,7 @@ import { HttpTestingController, provideHttpClientTesting, } from '@angular/common/http/testing' -import { EventEmitter } from '@angular/core' +import { EventEmitter, signal } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { NgbModal, NgbModalRef } from '@ng-bootstrap/ng-bootstrap' @@ -38,7 +38,6 @@ import { environment } from 'src/environments/environment' import { CorrespondentEditDialogComponent } from '../../common/edit-dialog/correspondent-edit-dialog/correspondent-edit-dialog.component' import { CustomFieldEditDialogComponent } from '../../common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component' import { DocumentTypeEditDialogComponent } from '../../common/edit-dialog/document-type-edit-dialog/document-type-edit-dialog.component' -import { EditDialogMode } from '../../common/edit-dialog/edit-dialog.component' import { StoragePathEditDialogComponent } from '../../common/edit-dialog/storage-path-edit-dialog/storage-path-edit-dialog.component' import { TagEditDialogComponent } from '../../common/edit-dialog/tag-edit-dialog/tag-edit-dialog.component' import { FilterableDropdownComponent } from '../../common/filterable-dropdown/filterable-dropdown.component' @@ -1146,7 +1145,7 @@ describe('BulkEditorComponent', () => { fixture.detectChanges() component.mergeSelected() expect(modal).not.toBeUndefined() - modal.componentInstance.metadataDocumentID = 3 + modal.componentInstance.metadataDocumentID.set(3) modal.componentInstance.confirm() let req = httpTestingController.expectOne( `${environment.apiBaseUrl}documents/merge/` @@ -1164,7 +1163,7 @@ describe('BulkEditorComponent', () => { ) // listAllFilteredIds // Test with Delete Originals enabled - modal.componentInstance.deleteOriginals = true + modal.componentInstance.deleteOriginals.set(true) modal.componentInstance.confirm() req = httpTestingController.expectOne( `${environment.apiBaseUrl}documents/merge/` @@ -1184,8 +1183,8 @@ describe('BulkEditorComponent', () => { expect(documentListViewService.selected.size).toEqual(0) // Test with archiveFallback enabled - modal.componentInstance.deleteOriginals = false - modal.componentInstance.archiveFallback = true + modal.componentInstance.deleteOriginals.set(false) + modal.componentInstance.archiveFallback.set(true) modal.componentInstance.confirm() req = httpTestingController.expectOne( `${environment.apiBaseUrl}documents/merge/` @@ -1339,7 +1338,7 @@ describe('BulkEditorComponent', () => { const modalInstance = { componentInstance: { - dialogMode: EditDialogMode.CREATE, + dialogMode: { set: jest.fn() }, object: { name }, succeeded: of(newTag), }, @@ -1382,7 +1381,7 @@ describe('BulkEditorComponent', () => { const modalInstance = { componentInstance: { - dialogMode: EditDialogMode.CREATE, + dialogMode: { set: jest.fn() }, object: { name }, succeeded: of(newCorrespondent), }, @@ -1431,7 +1430,7 @@ describe('BulkEditorComponent', () => { const modalInstance = { componentInstance: { - dialogMode: EditDialogMode.CREATE, + dialogMode: { set: jest.fn() }, object: { name }, succeeded: of(newDocumentType), }, @@ -1477,7 +1476,7 @@ describe('BulkEditorComponent', () => { const modalInstance = { componentInstance: { - dialogMode: EditDialogMode.CREATE, + dialogMode: { set: jest.fn() }, object: { name }, succeeded: of(newStoragePath), }, @@ -1531,7 +1530,7 @@ describe('BulkEditorComponent', () => { const modalInstance = { componentInstance: { - dialogMode: EditDialogMode.CREATE, + dialogMode: { set: jest.fn() }, object: { name }, succeeded: of(newCustomField), }, @@ -1629,15 +1628,18 @@ describe('BulkEditorComponent', () => { close: jest.fn(), componentInstance: { documents: [], + setDocuments(docs) { + this.documents = docs + }, confirmClicked, payload: { document_ids: [5, 7], file_version: 'archive', expiration_days: 7, }, - loading: false, + loading: signal(false), buttonsEnabled: true, - copied: false, + copied: signal(false), }, } @@ -1667,7 +1669,7 @@ describe('BulkEditorComponent', () => { file_version: 'archive', expiration_days: 7, }) - expect(dialogInstance.loading).toBe(false) + expect(dialogInstance.loading()).toBe(false) expect(dialogInstance.buttonsEnabled).toBe(false) expect(dialogInstance.createdBundle).toEqual({ id: 42 }) expect(typeof dialogInstance.onOpenManage).toBe('function') @@ -1698,13 +1700,16 @@ describe('BulkEditorComponent', () => { const modalRef: Partial = { componentInstance: { documents: [], + setDocuments(docs) { + this.documents = docs + }, confirmClicked, payload: { document_ids: [9], file_version: 'original', expiration_days: null, }, - loading: false, + loading: signal(false), buttonsEnabled: true, }, } @@ -1726,7 +1731,7 @@ describe('BulkEditorComponent', () => { $localize`Share link bundle creation is not available yet.`, expect.any(Error) ) - expect(dialogInstance.loading).toBe(false) + expect(dialogInstance.loading()).toBe(false) expect(dialogInstance.buttonsEnabled).toBe(true) openSpy.mockRestore() }) diff --git a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts index fc86e13a1..41a5e4d0f 100644 --- a/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts +++ b/src-ui/src/app/components/document-list/bulk-editor/bulk-editor.component.ts @@ -274,7 +274,7 @@ export class BulkEditorComponent overrideSelection?: DocumentSelectionQuery ) { if (modal) { - modal.componentInstance.buttonsEnabled = false + this.setModalButtonsEnabled(modal, false) } this.documentService .bulkEdit(overrideSelection ?? this.getSelectionQuery(), method, args) @@ -291,7 +291,7 @@ export class BulkEditorComponent options: { deleteOriginals?: boolean } = {} ) { if (modal) { - modal.componentInstance.buttonsEnabled = false + this.setModalButtonsEnabled(modal, false) } request.pipe(first()).subscribe({ next: () => { @@ -321,7 +321,7 @@ export class BulkEditorComponent private handleOperationError(modal: NgbModalRef, error: any) { if (modal) { - modal.componentInstance.buttonsEnabled = true + this.setModalButtonsEnabled(modal, true) } this.toastService.showError( $localize`Error executing bulk operation`, @@ -329,6 +329,15 @@ export class BulkEditorComponent ) } + private setModalButtonsEnabled(modal: NgbModalRef, enabled: boolean) { + const buttonsEnabled = modal.componentInstance.buttonsEnabled + if (typeof buttonsEnabled?.set === 'function') { + buttonsEnabled.set(enabled) + } else { + modal.componentInstance.buttonsEnabled = enabled + } + } + private applySelectionData( items: SelectionDataItem[], selectionModel: FilterableDropdownSelectionModel @@ -744,7 +753,7 @@ export class BulkEditorComponent let modal = this.modalService.open(TagEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) modal.componentInstance.object = { name } modal.componentInstance.succeeded .pipe( @@ -765,7 +774,7 @@ export class BulkEditorComponent let modal = this.modalService.open(CorrespondentEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) modal.componentInstance.object = { name } modal.componentInstance.succeeded .pipe( @@ -788,7 +797,7 @@ export class BulkEditorComponent let modal = this.modalService.open(DocumentTypeEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) modal.componentInstance.object = { name } modal.componentInstance.succeeded .pipe( @@ -809,7 +818,7 @@ export class BulkEditorComponent let modal = this.modalService.open(StoragePathEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) modal.componentInstance.object = { name } modal.componentInstance.succeeded .pipe( @@ -830,7 +839,7 @@ export class BulkEditorComponent let modal = this.modalService.open(CustomFieldEditDialogComponent, { backdrop: 'static', }) - modal.componentInstance.dialogMode = EditDialogMode.CREATE + modal.componentInstance.dialogMode.set(EditDialogMode.CREATE) modal.componentInstance.object = { name } modal.componentInstance.succeeded .pipe( @@ -943,7 +952,7 @@ export class BulkEditorComponent }) modal.componentInstance.confirmClicked.subscribe( ({ permissions, merge }) => { - modal.componentInstance.buttonsEnabled = false + modal.componentInstance.buttonsEnabled.set(false) this.executeBulkEditMethod(modal, 'set_permissions', { ...permissions, merge, @@ -962,7 +971,7 @@ export class BulkEditorComponent rotateDialog.messageBold = $localize`This operation will add rotated versions of the ${this.getSelectionSize()} document(s).` rotateDialog.btnClass = 'btn-danger' rotateDialog.btnCaption = $localize`Proceed` - rotateDialog.documentID = Array.from(this.list.selected)[0] + rotateDialog.documentID.set(Array.from(this.list.selected)[0]) rotateDialog.confirmClicked .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { @@ -985,24 +994,24 @@ export class BulkEditorComponent mergeDialog.title = $localize`Merge confirm` mergeDialog.messageBold = $localize`This operation will merge ${this.getSelectionSize()} selected documents into a new document.` mergeDialog.btnCaption = $localize`Proceed` - mergeDialog.documentIDs = Array.from(this.list.selected) + mergeDialog.documentIDs.set(Array.from(this.list.selected)) mergeDialog.confirmClicked .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { const args: MergeDocumentsRequest = {} - if (mergeDialog.metadataDocumentID > -1) { - args.metadata_document_id = mergeDialog.metadataDocumentID + if (mergeDialog.metadataDocumentID() > -1) { + args.metadata_document_id = mergeDialog.metadataDocumentID() } - if (mergeDialog.deleteOriginals) { + if (mergeDialog.deleteOriginals()) { args.delete_originals = true } - if (mergeDialog.archiveFallback) { + if (mergeDialog.archiveFallback()) { args.archive_fallback = true } mergeDialog.buttonsEnabled = false this.executeDocumentAction( modal, - this.documentService.mergeDocuments(mergeDialog.documentIDs, args), + this.documentService.mergeDocuments(mergeDialog.documentIDs(), args), { deleteOriginals: !!args.delete_originals } ) this.toastService.showInfo( @@ -1058,21 +1067,21 @@ export class BulkEditorComponent const selectedDocuments = this.list.documents.filter((d) => this.list.selected.has(d.id) ) - dialog.documents = selectedDocuments + dialog.setDocuments(selectedDocuments) dialog.confirmClicked .pipe(takeUntil(this.unsubscribeNotifier)) .subscribe(() => { - dialog.loading = true + dialog.loading.set(true) dialog.buttonsEnabled = false this.shareLinkBundleService .createBundle(dialog.payload) .pipe(first()) .subscribe({ next: (result) => { - dialog.loading = false + dialog.loading.set(false) dialog.buttonsEnabled = false dialog.createdBundle = result - dialog.copied = false + dialog.copied.set(false) dialog.payload = null dialog.onOpenManage = () => { modal.close() @@ -1083,7 +1092,7 @@ export class BulkEditorComponent ) }, error: (error) => { - dialog.loading = false + dialog.loading.set(false) dialog.buttonsEnabled = true this.toastService.showError( $localize`Share link bundle creation is not available yet.`, @@ -1109,7 +1118,7 @@ export class BulkEditorComponent const modal = this.modalService.open(EmailDocumentDialogComponent, { backdrop: 'static', }) - modal.componentInstance.documentIds = Array.from(this.list.selected) - modal.componentInstance.hasArchiveVersion = allHaveArchiveVersion + modal.componentInstance.documentIds.set(Array.from(this.list.selected)) + modal.componentInstance.hasArchiveVersion.set(allHaveArchiveVersion) } } diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html index 1c69d0f53..08d232185 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.html @@ -1,13 +1,13 @@ -
              +
              - @if (document) { + @if (document()) {
              - - + +
              } @else { @@ -19,20 +19,20 @@
              - @if (document) { - @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) { + @if (document()) { + @if (displayFields().includes(DisplayField.CORRESPONDENT) && document().correspondent) { @if (clickCorrespondent.observers.length ) { - {{document.correspondent | correspondentName | async}} + {{document().correspondent | correspondentName | async}} } @else { - {{document.correspondent | correspondentName | async}} + {{document().correspondent | correspondentName | async}} } - @if (displayFields.includes(DisplayField.TITLE)) {:} + @if (displayFields().includes(DisplayField.TITLE)) {:} } - @if (displayFields.includes(DisplayField.TITLE)) { - {{document.title | documentTitle}} + @if (displayFields().includes(DisplayField.TITLE)) { + {{document().title | documentTitle}} } - @if (displayFields.includes(DisplayField.TAGS)) { - @for (tagID of document.tags; track tagID) { + @if (displayFields().includes(DisplayField.TAGS)) { + @for (tagID of document().tags; track tagID) { } } @@ -42,9 +42,9 @@

              - @if (document) { + @if (document()) { @if (hasSearchHighlights) { - + } @for (highlight of searchNoteHighlights; track highlight) { @@ -64,14 +64,14 @@

              - @if (document) { + @if (document()) { More like this - + Open - + View @@ -86,76 +86,76 @@
              - @if (document) { - @if (displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) { - } - @if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) { + @if (displayFields().includes(DisplayField.DOCUMENT_TYPE) && document().document_type) { } - @if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) { + @if (displayFields().includes(DisplayField.STORAGE_PATH) && document().storage_path) { } - @if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) { + @if (displayFields().includes(DisplayField.ASN) && document().archive_serial_number | isNumber) {
              - #{{document.archive_serial_number}} + #{{document().archive_serial_number}}
              } - @if (displayFields.includes(DisplayField.CREATED) || displayFields.includes(DisplayField.ADDED)) { + @if (displayFields().includes(DisplayField.CREATED) || displayFields().includes(DisplayField.ADDED)) {
              - Created: {{ document.created | customDate }} - Added: {{ document.added | customDate }} - Modified: {{ document.modified | customDate }} + Created: {{ document().created | customDate }} + Added: {{ document().added | customDate }} + Modified: {{ document().modified | customDate }}
              - @if (displayFields.includes(DisplayField.CREATED)) { + @if (displayFields().includes(DisplayField.CREATED)) {
              - {{document.created | customDate:'mediumDate'}} + {{document().created | customDate:'mediumDate'}}
              } - @if (displayFields.includes(DisplayField.ADDED)) { + @if (displayFields().includes(DisplayField.ADDED)) {
              - {{document.added | customDate:'mediumDate'}} + {{document().added | customDate:'mediumDate'}}
              } } - @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) { + @if (displayFields().includes(DisplayField.PAGE_COUNT) && document().page_count) {
              - {document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}} + {document().page_count, plural, =1 {1 page} other {{{document().page_count}} pages}}
              } - @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { + @if (displayFields().includes(DisplayField.OWNER) && document().owner && document().owner !== settingsService.currentUser().id) {
              - {{document.owner | username | async}} + {{document().owner | username | async}}
              } - @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) { + @if (displayFields().includes(DisplayField.SHARED) && document().is_shared_by_requester) {
              Shared
              } - @if (document.__search_hit__?.score) { + @if (document().__search_hit__?.score) {
              Score: - +
              } - @for (field of document.custom_fields; track field.field) { - @if (displayFields.includes(DisplayField.CUSTOM_FIELD + field.field)) { + @for (field of document().custom_fields; track field.field) { + @if (displayFields().includes(DisplayField.CUSTOM_FIELD + field.field)) {
              - +
              } diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss index 359346867..b89b6e4a8 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.scss @@ -2,6 +2,26 @@ overflow-wrap: anywhere; } +.fade.show { + animation: pngx-entry-fade 160ms ease-out; +} + +@keyframes pngx-entry-fade { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@media (prefers-reduced-motion: reduce) { + .fade.show { + animation: none; + } +} + .doc-img-container { position: relative; } diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts index e4e2bca74..d50bba65f 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.spec.ts @@ -67,16 +67,19 @@ describe('DocumentCardLargeComponent', () => { fixture = TestBed.createComponent(DocumentCardLargeComponent) component = fixture.componentInstance - component.document = doc + fixture.componentRef.setInput('document', { + ...doc, + tags: [...doc.tags], + notes: [...doc.notes], + }) fixture.detectChanges() jest.useFakeTimers() }) it('should show the card', () => { - expect(component.show).toBeFalsy() + expect(component.show()).toBeTruthy() component.ngAfterViewInit() - jest.advanceTimersByTime(100) - expect(component.show).toBeTruthy() + expect(component.show()).toBeTruthy() }) it('should display a document', () => { @@ -91,25 +94,34 @@ describe('DocumentCardLargeComponent', () => { it('should display search hits with colored score', () => { // high - component.document.__search_hit__ = { - score: 0.9, - rank: 1, - highlights: 'cheesecake', - } + fixture.componentRef.setInput('document', { + ...component.document(), + __search_hit__: { + score: 0.9, + rank: 1, + highlights: 'cheesecake', + }, + }) fixture.detectChanges() let search_hit = fixture.debugElement.query(By.css('.search-score')) expect(search_hit).not.toBeUndefined() expect(component.searchScoreClass).toEqual('success') // medium - component.document.__search_hit__.score = 0.6 + fixture.componentRef.setInput('document', { + ...component.document(), + __search_hit__: { ...component.document().__search_hit__, score: 0.6 }, + }) fixture.detectChanges() search_hit = fixture.debugElement.query(By.css('.search-score')) expect(search_hit).not.toBeUndefined() expect(component.searchScoreClass).toEqual('warning') // low - component.document.__search_hit__.score = 0.1 + fixture.componentRef.setInput('document', { + ...component.document(), + __search_hit__: { ...component.document().__search_hit__, score: 0.1 }, + }) fixture.detectChanges() search_hit = fixture.debugElement.query(By.css('.search-score')) expect(search_hit).not.toBeUndefined() @@ -117,23 +129,29 @@ describe('DocumentCardLargeComponent', () => { }) it('should display note highlights', () => { - component.document.__search_hit__ = { - score: 0.9, - rank: 1, - note_highlights: 'bananas', - } + fixture.componentRef.setInput('document', { + ...component.document(), + __search_hit__: { + score: 0.9, + rank: 1, + note_highlights: 'bananas', + }, + }) fixture.detectChanges() expect(fixture.nativeElement.textContent).toContain('bananas') expect(component.searchNoteHighlights).toContain('bananas') }) it('should fall back to document content when a search hit has no highlights', () => { - component.document.__search_hit__ = { - score: 0.9, - rank: 1, - highlights: '', - note_highlights: null, - } + fixture.componentRef.setInput('document', { + ...component.document(), + __search_hit__: { + score: 0.9, + rank: 1, + highlights: '', + note_highlights: null, + }, + }) fixture.detectChanges() expect(fixture.nativeElement.textContent).toContain('Cupcake ipsum') diff --git a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts index 8f77489ec..1dac73959 100644 --- a/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts +++ b/src-ui/src/app/components/document-list/document-card-large/document-card-large.component.ts @@ -3,10 +3,10 @@ import { AfterViewInit, Component, EventEmitter, - Input, Output, ViewChild, inject, + input, } from '@angular/core' import { RouterModule } from '@angular/router' import { @@ -14,7 +14,6 @@ import { NgbTooltipModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { delay, of } from 'rxjs' import { DEFAULT_DISPLAY_FIELDS, DisplayField, @@ -65,15 +64,14 @@ export class DocumentCardLargeComponent { private documentService = inject(DocumentService) settingsService = inject(SettingsService) + readonly selected = input(false) + readonly displayFields = input( + DEFAULT_DISPLAY_FIELDS.map((f) => f.id) + ) + readonly document = input(undefined) DisplayField = DisplayField - @Input() - selected = false - - @Input() - displayFields: string[] = DEFAULT_DISPLAY_FIELDS.map((f) => f.id) - @Output() toggleSelected = new EventEmitter() @@ -81,9 +79,6 @@ export class DocumentCardLargeComponent return this.toggleSelected.observers.length > 0 } - @Input() - document: Document - @Output() dblClickDocument = new EventEmitter() @@ -108,18 +103,15 @@ export class DocumentCardLargeComponent popoverHidden = true ngAfterViewInit(): void { - of(true) - .pipe(delay(50)) - .subscribe(() => { - this.show = true - }) + this.show.set(true) } get searchScoreClass() { - if (this.document.__search_hit__) { - if (this.document.__search_hit__.score > 0.7) { + const document = this.document() + if (document.__search_hit__) { + if (document.__search_hit__.score > 0.7) { return 'success' - } else if (this.document.__search_hit__.score > 0.3) { + } else if (document.__search_hit__.score > 0.3) { return 'warning' } else { return 'danger' @@ -129,12 +121,10 @@ export class DocumentCardLargeComponent get searchNoteHighlights() { let highlights = [] - if ( - this.document['__search_hit__'] && - this.document['__search_hit__'].note_highlights - ) { + const document = this.document() + if (document?.['__search_hit__']?.note_highlights) { // only show notes with a match - highlights = (this.document['__search_hit__'].note_highlights as string) + highlights = (document['__search_hit__'].note_highlights as string) .split(',') .filter((highlight) => highlight.includes(' 500 ? '...' : '') + document.content.substring(0, 500) + + (document.content.length > 500 ? '...' : '') ) } get hasSearchHighlights() { - return Boolean(this.document?.__search_hit__?.highlights?.trim()?.length) + return Boolean(this.document()?.__search_hit__?.highlights?.trim()?.length) } get shouldShowContentFallback() { + const document = this.document() return ( - this.document?.__search_hit__?.score == null || + document?.__search_hit__?.score == null || (!this.hasSearchHighlights && this.searchNoteHighlights.length === 0) ) } diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html index b3a29aed4..a7a30c790 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.html @@ -1,21 +1,21 @@ -
              -
              +
              +
              - @if (document) { + @if (document()) {
              - - + +
              } @else {
              } - @if (document && displayFields?.includes(DisplayField.TAGS)) { -
              + @if (document() && displayFields().includes(DisplayField.TAGS)) { +
              @for (tagID of tagIDs; track tagID) { } @@ -28,23 +28,23 @@ }
              - @if (document && displayFields.includes(DisplayField.NOTES) && notesEnabled && document.notes.length) { -
              + @if (document() && displayFields().includes(DisplayField.NOTES) && notesEnabled && document().notes.length) { + - {{document.notes.length}} + {{document().notes.length}} }

              - @if (document) { - @if (displayFields.includes(DisplayField.CORRESPONDENT) && document.correspondent) { - {{document.correspondent | correspondentName | async}} - @if (displayFields.includes(DisplayField.TITLE)) {:} + @if (document()) { + @if (displayFields().includes(DisplayField.CORRESPONDENT) && document().correspondent) { + {{document().correspondent | correspondentName | async}} + @if (displayFields().includes(DisplayField.TITLE)) {:} } - @if (displayFields.includes(DisplayField.TITLE)) { - {{document.title | documentTitle}} + @if (displayFields().includes(DisplayField.TITLE)) { + {{document().title | documentTitle}} } } @else {

              @@ -54,82 +54,82 @@
              - @if (document) { - @if (displayFields.includes(DisplayField.DOCUMENT_TYPE) && document.document_type) { + @if (document()) { + @if (displayFields().includes(DisplayField.DOCUMENT_TYPE) && document().document_type) { } - @if (displayFields.includes(DisplayField.STORAGE_PATH) && document.storage_path) { + @if (displayFields().includes(DisplayField.STORAGE_PATH) && document().storage_path) { } - @if (displayFields.includes(DisplayField.CREATED)) { + @if (displayFields().includes(DisplayField.CREATED)) {
              - Created: {{ document.created | customDate }} - Added: {{ document.added | customDate }} - Modified: {{ document.modified | customDate }} + Created: {{ document().created | customDate }} + Added: {{ document().added | customDate }} + Modified: {{ document().modified | customDate }}
              - {{document.created | customDate:'mediumDate'}} + {{document().created | customDate:'mediumDate'}}
              } - @if (displayFields.includes(DisplayField.ADDED)) { + @if (displayFields().includes(DisplayField.ADDED)) {
              - Created: {{ document.created | customDate }} - Added: {{ document.added | customDate }} - Modified: {{ document.modified | customDate }} + Created: {{ document().created | customDate }} + Added: {{ document().added | customDate }} + Modified: {{ document().modified | customDate }}
              - {{document.added | customDate:'mediumDate'}} + {{document().added | customDate:'mediumDate'}}
              } - @if (displayFields.includes(DisplayField.PAGE_COUNT) && document.page_count) { + @if (displayFields().includes(DisplayField.PAGE_COUNT) && document().page_count) {
              - {document.page_count, plural, =1 {1 page} other {{{document.page_count}} pages}} + {document().page_count, plural, =1 {1 page} other {{{document().page_count}} pages}}
              } - @if (displayFields.includes(DisplayField.ASN) && document.archive_serial_number | isNumber) { + @if (displayFields().includes(DisplayField.ASN) && document().archive_serial_number | isNumber) {
              - #{{document.archive_serial_number}} + #{{document().archive_serial_number}}
              } - @if (displayFields.includes(DisplayField.OWNER) && document.owner && document.owner !== settingsService.currentUser.id) { + @if (displayFields().includes(DisplayField.OWNER) && document().owner && document().owner !== settingsService.currentUser().id) {
              - {{document.owner | username | async}} + {{document().owner | username | async}}
              } - @if (displayFields.includes(DisplayField.SHARED) && document.is_shared_by_requester) { + @if (displayFields().includes(DisplayField.SHARED) && document().is_shared_by_requester) {
              Shared
              } - @for (field of document.custom_fields; track field.field) { - @if (displayFields.includes(DisplayField.CUSTOM_FIELD + field.field)) { + @for (field of document().custom_fields; track field.field) { + @if (displayFields().includes(DisplayField.CUSTOM_FIELD + field.field)) {
              - +
              } } @@ -139,11 +139,11 @@
              - @if (document) { - + @if (document()) { + - + diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss index dce77802e..6f2260e47 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.scss @@ -2,6 +2,26 @@ font-size: 90%; } +.fade.show { + animation: pngx-entry-fade 160ms ease-out; +} + +@keyframes pngx-entry-fade { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} + +@media (prefers-reduced-motion: reduce) { + .fade.show { + animation: none; + } +} + .doc-img { object-fit: cover; object-position: top left; diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts index 982b3980b..45b812462 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.spec.ts @@ -46,16 +46,15 @@ describe('DocumentCardSmallComponent', () => { fixture = TestBed.createComponent(DocumentCardSmallComponent) component = fixture.componentInstance - component.document = Object.assign({}, doc) + fixture.componentRef.setInput('document', Object.assign({}, doc)) fixture.detectChanges() jest.useFakeTimers() }) it('should show the card', () => { - expect(component.show).toBeFalsy() + expect(component.show()).toBeTruthy() component.ngAfterViewInit() - jest.advanceTimersByTime(100) - expect(component.show).toBeTruthy() + expect(component.show()).toBeTruthy() }) it('should display page count', () => { @@ -67,7 +66,10 @@ describe('DocumentCardSmallComponent', () => { expect( fixture.debugElement.queryAll(By.directive(TagComponent)) ).toHaveLength(5) - component.document.tags = [1, 2] + fixture.componentRef.setInput('document', { + ...component.document(), + tags: [1, 2], + }) fixture.detectChanges() expect( fixture.debugElement.queryAll(By.directive(TagComponent)) @@ -75,7 +77,10 @@ describe('DocumentCardSmallComponent', () => { }) it('should increase limit tags to 6 if no notes', () => { - component.document.notes = [] + fixture.componentRef.setInput('document', { + ...component.document(), + notes: [], + }) fixture.detectChanges() expect( fixture.debugElement.queryAll(By.directive(TagComponent)) @@ -85,7 +90,10 @@ describe('DocumentCardSmallComponent', () => { it('should clear hidden tag counter when tag count falls below the limit', () => { expect(component.moreTags).toEqual(3) - component.document.tags = [1, 2, 3, 4, 5, 6] + fixture.componentRef.setInput('document', { + ...component.document(), + tags: [1, 2, 3, 4, 5, 6], + }) fixture.detectChanges() expect(component.moreTags).toBeNull() diff --git a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts index 05f84d752..1e45b982a 100644 --- a/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts +++ b/src-ui/src/app/components/document-list/document-card-small/document-card-small.component.ts @@ -3,10 +3,10 @@ import { AfterViewInit, Component, EventEmitter, - Input, Output, ViewChild, inject, + input, } from '@angular/core' import { RouterModule } from '@angular/router' import { @@ -14,8 +14,6 @@ import { NgbTooltipModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { of } from 'rxjs' -import { delay } from 'rxjs/operators' import { DEFAULT_DISPLAY_FIELDS, DisplayField, @@ -66,21 +64,17 @@ export class DocumentCardSmallComponent { private documentService = inject(DocumentService) settingsService = inject(SettingsService) + readonly selected = input(false) + readonly document = input(undefined) + readonly displayFields = input( + DEFAULT_DISPLAY_FIELDS.map((f) => f.id) + ) DisplayField = DisplayField - @Input() - selected = false - @Output() toggleSelected = new EventEmitter() - @Input() - document: Document - - @Input() - displayFields: string[] = DEFAULT_DISPLAY_FIELDS.map((f) => f.id) - @Output() dblClickDocument = new EventEmitter() @@ -96,16 +90,18 @@ export class DocumentCardSmallComponent @Output() clickStoragePath = new EventEmitter() - moreTags: number = null + get moreTags(): number { + const document = this.document() + const limit = document?.notes.length > 0 ? 6 : 7 + return document?.tags.length > limit + ? document.tags.length - (limit - 1) + : null + } @ViewChild('popupPreview') popupPreview: PreviewPopupComponent ngAfterViewInit(): void { - of(true) - .pipe(delay(50)) - .subscribe(() => { - this.show = true - }) + this.show.set(true) } getIsThumbInverted() { @@ -113,21 +109,20 @@ export class DocumentCardSmallComponent } getThumbUrl() { - return this.documentService.getThumbUrl(this.document.id) + return this.documentService.getThumbUrl(this.document().id) } getDownloadUrl() { - return this.documentService.getDownloadUrl(this.document.id) + return this.documentService.getDownloadUrl(this.document().id) } get tagIDs() { - const limit = this.document.notes.length > 0 ? 6 : 7 - if (this.document.tags.length > limit) { - this.moreTags = this.document.tags.length - (limit - 1) - return this.document.tags.slice(0, limit - 1) + const document = this.document() + const limit = document.notes.length > 0 ? 6 : 7 + if (document.tags.length > limit) { + return document.tags.slice(0, limit - 1) } else { - this.moreTags = null - return this.document.tags + return document.tags } } diff --git a/src-ui/src/app/components/document-list/document-list.component.html b/src-ui/src/app/components/document-list/document-list.component.html index aadec7d77..b33322cdf 100644 --- a/src-ui/src/app/components/document-list/document-list.component.html +++ b/src-ui/src/app/components/document-list/document-list.component.html @@ -36,7 +36,7 @@
              - @for (field of settingsService.allDisplayFields; track field.id) { + @for (field of settingsService.allDisplayFields(); track field.id) {
              diff --git a/src-ui/src/app/components/document-list/document-list.component.spec.ts b/src-ui/src/app/components/document-list/document-list.component.spec.ts index 9ea7f27de..3ef7a7ffa 100644 --- a/src-ui/src/app/components/document-list/document-list.component.spec.ts +++ b/src-ui/src/app/components/document-list/document-list.component.spec.ts @@ -730,7 +730,9 @@ describe('DocumentListComponent', () => { showInSideBar: true, }) expect(updateVisibilitySpy).not.toHaveBeenCalled() - expect(openModal.componentInstance.error).toEqual({ filter_rules: ['11'] }) + expect(openModal.componentInstance.error()).toEqual({ + filter_rules: ['11'], + }) }) it('should detect saved view changes', () => { @@ -777,7 +779,7 @@ describe('DocumentListComponent', () => { }) it('should hide columns if no perms or notes disabled', () => { - jest.spyOn(permissionService, 'currentUserCan').mockReturnValue(true) + permissionService.initialize([], { is_superuser: true } as any) jest.spyOn(documentListService, 'documents', 'get').mockReturnValue(docs) expect(documentListService.sortField).toEqual('created') @@ -798,7 +800,7 @@ describe('DocumentListComponent', () => { ).toHaveLength(9) // insufficient perms - jest.spyOn(permissionService, 'currentUserCan').mockReturnValue(false) + permissionService.initialize([], { is_superuser: false } as any) fixture.detectChanges() expect( fixture.debugElement.queryAll(By.directive(SortableDirective)) @@ -837,11 +839,10 @@ describe('DocumentListComponent', () => { it('should get custom field title', () => { fixture.detectChanges() - jest - .spyOn(settingsService, 'allDisplayFields', 'get') - .mockReturnValue([ - { id: 'custom_field_1' as any, name: 'Custom Field 1' }, - ]) + const mockDisplayFields = [ + { id: 'custom_field_1' as any, name: 'Custom Field 1' }, + ] + settingsService.allDisplayFields = jest.fn(() => mockDisplayFields) as any expect(component.getDisplayCustomFieldTitle('custom_field_1')).toEqual( 'Custom Field 1' ) diff --git a/src-ui/src/app/components/document-list/document-list.component.ts b/src-ui/src/app/components/document-list/document-list.component.ts index eb453d4dc..ee8f3bd55 100644 --- a/src-ui/src/app/components/document-list/document-list.component.ts +++ b/src-ui/src/app/components/document-list/document-list.component.ts @@ -255,7 +255,7 @@ export class DocumentListComponent } public getDisplayCustomFieldTitle(field: string) { - return this.settingsService.allDisplayFields.find((f) => f.id === field) + return this.settingsService.allDisplayFields().find((f) => f.id === field) ?.name } @@ -449,9 +449,11 @@ export class DocumentListComponent let modal = this.modalService.open(SaveViewConfigDialogComponent, { backdrop: 'static', }) - modal.componentInstance.defaultName = this.filterEditor.generateFilterName() + modal.componentInstance.setDefaultName( + this.filterEditor.generateFilterName() + ) modal.componentInstance.saveClicked.pipe(first()).subscribe((formValue) => { - modal.componentInstance.buttonsEnabled = false + modal.componentInstance.buttonsEnabled.set(false) let savedView: SavedView = { name: formValue.name, filter_rules: this.list.filterRules, @@ -502,8 +504,8 @@ export class DocumentListComponent if (error.filter_rules) { error.filter_rules = error.filter_rules.map((r) => r.value) } - modal.componentInstance.error = error - modal.componentInstance.buttonsEnabled = true + modal.componentInstance.error.set(error) + modal.componentInstance.buttonsEnabled.set(true) }, }) }) @@ -570,6 +572,7 @@ export class DocumentListComponent } get notesEnabled(): boolean { + this.settingsService.trackChanges() return this.settingsService.get(SETTINGS_KEYS.NOTES_ENABLED) } diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html index f4a7938b7..2a03727f6 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.html @@ -1,6 +1,6 @@
              -
              +
              @@ -36,7 +36,7 @@
              @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Tag) && tagSelectionModel.items.length > 0) { - } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.Correspondent) && correspondentSelectionModel.items.length > 0) { - } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.DocumentType) && documentTypeSelectionModel.items.length > 0) { - } @if (permissionsService.currentUserCan(PermissionAction.View, PermissionType.StoragePath) && storagePathSelectionModel.items.length > 0) { - 0) { - } - - diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts index d75e38630..63d61fbad 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.spec.ts @@ -4,12 +4,7 @@ import { HttpTestingController, provideHttpClientTesting, } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { By } from '@angular/platform-browser' import { RouterModule } from '@angular/router' @@ -189,8 +184,13 @@ describe('FilterEditorComponent', () => { let httpTestingController: HttpTestingController let searchService: SearchService - beforeEach(fakeAsync(() => { - TestBed.configureTestingModule({ + const tick = (ms: number = 0) => { + jest.advanceTimersByTime(ms) + fixture.detectChanges() + } + + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [ RouterModule, NgbDropdownModule, @@ -260,7 +260,7 @@ describe('FilterEditorComponent', () => { documentService = TestBed.inject(DocumentService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = users[0] + settingsService.currentUser.set(users[0]) permissionsService = TestBed.inject(PermissionsService) searchService = TestBed.inject(SearchService) jest @@ -275,8 +275,13 @@ describe('FilterEditorComponent', () => { component = fixture.componentInstance component.filterRules = [] fixture.detectChanges() - tick() - })) + await fixture.whenStable() + jest.useFakeTimers() + }) + + afterEach(() => { + jest.useRealTimers() + }) it('should not attempt to retrieve objects if user does not have permissions', () => { jest.spyOn(permissionsService, 'currentUserCan').mockReset() @@ -298,7 +303,7 @@ describe('FilterEditorComponent', () => { // SET filterRules - it('should ingest text filter rules for doc title', fakeAsync(() => { + it('should ingest text filter rules for doc title', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -308,9 +313,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('foo') expect(component.textFilterTarget).toEqual('title') // TEXT_FILTER_TARGET_TITLE - })) + }) - it('should ingest text filter rules for doc title + content', fakeAsync(() => { + it('should ingest text filter rules for doc title + content', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -320,9 +325,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('foo') expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT - })) + }) - it('should ingest legacy text filter rules for doc title + content', fakeAsync(() => { + it('should ingest legacy text filter rules for doc title + content', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -332,9 +337,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('legacy foo') expect(component.textFilterTarget).toEqual('title-content') // TEXT_FILTER_TARGET_TITLE_CONTENT - })) + }) - it('should ingest text filter rules for doc asn', fakeAsync(() => { + it('should ingest text filter rules for doc asn', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -344,9 +349,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('foo') expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN - })) + }) - it('should ingest text filter rules for custom fields', fakeAsync(() => { + it('should ingest text filter rules for custom fields', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -356,9 +361,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('foo') expect(component.textFilterTarget).toEqual('custom-fields') // TEXT_FILTER_TARGET_CUSTOM_FIELDS - })) + }) - it('should ingest text filter rules for doc asn is null', fakeAsync(() => { + it('should ingest text filter rules for doc asn is null', () => { expect(component.textFilterTarget).toEqual('title-content') expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS component.filterRules = [ @@ -369,9 +374,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN expect(component.textFilterModifier).toEqual('is null') // TEXT_FILTER_MODIFIER_NULL - })) + }) - it('should ingest text filter rules for doc asn is not null', fakeAsync(() => { + it('should ingest text filter rules for doc asn is not null', () => { expect(component.textFilterTarget).toEqual('title-content') expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS component.filterRules = [ @@ -382,9 +387,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN expect(component.textFilterModifier).toEqual('not null') // TEXT_FILTER_MODIFIER_NOTNULL - })) + }) - it('should ingest text filter rules for doc asn greater than', fakeAsync(() => { + it('should ingest text filter rules for doc asn greater than', () => { expect(component.textFilterTarget).toEqual('title-content') expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS component.filterRules = [ @@ -395,9 +400,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN expect(component.textFilterModifier).toEqual('greater') // TEXT_FILTER_MODIFIER_GT - })) + }) - it('should ingest text filter rules for doc asn less than', fakeAsync(() => { + it('should ingest text filter rules for doc asn less than', () => { expect(component.textFilterTarget).toEqual('title-content') expect(component.textFilterModifier).toEqual('equals') // TEXT_FILTER_MODIFIER_EQUALS component.filterRules = [ @@ -408,9 +413,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilterTarget).toEqual('asn') // TEXT_FILTER_TARGET_ASN expect(component.textFilterModifier).toEqual('less') // TEXT_FILTER_MODIFIER_LT - })) + }) - it('should ingest text filter rules for mime type', fakeAsync(() => { + it('should ingest text filter rules for mime type', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -420,9 +425,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('pdf') expect(component.textFilterTarget).toEqual('mime-type') // TEXT_FILTER_TARGET_MIME_TYPE - })) + }) - it('should ingest text filter rules for fulltext query', fakeAsync(() => { + it('should ingest text filter rules for fulltext query', () => { expect(component.textFilter).toEqual(null) component.filterRules = [ { @@ -432,9 +437,9 @@ describe('FilterEditorComponent', () => { ] expect(component.textFilter).toEqual('foo,bar') expect(component.textFilterTarget).toEqual('fulltext-query') // TEXT_FILTER_TARGET_FULLTEXT_QUERY - })) + }) - it('should ingest text filter rules for fulltext query that include date created', fakeAsync(() => { + it('should ingest text filter rules for fulltext query that include date created', () => { expect(component.dateCreatedRelativeDate).toBeNull() component.filterRules = [ { @@ -444,9 +449,9 @@ describe('FilterEditorComponent', () => { ] expect(component.dateCreatedRelativeDate).toEqual(1) // RELATIVE_DATE_QUERYSTRINGS['-1 week to now'] expect(component.textFilter).toBeNull() - })) + }) - it('should ingest text filter rules for fulltext query that include date added', fakeAsync(() => { + it('should ingest text filter rules for fulltext query that include date added', () => { expect(component.dateAddedRelativeDate).toBeNull() component.filterRules = [ { @@ -456,9 +461,9 @@ describe('FilterEditorComponent', () => { ] expect(component.dateAddedRelativeDate).toEqual(1) // RELATIVE_DATE_QUERYSTRINGS['-1 week to now'] expect(component.textFilter).toBeNull() - })) + }) - it('should ingest text filter content with relative dates that are not in quick list', fakeAsync(() => { + it('should ingest text filter content with relative dates that are not in quick list', () => { expect(component.dateAddedRelativeDate).toBeNull() component.filterRules = [ { @@ -478,9 +483,9 @@ describe('FilterEditorComponent', () => { ] expect(component.dateCreatedRelativeDate).toBeNull() expect(component.textFilter).toEqual('created:[-2 week to now]') - })) + }) - it('should ingest text filter rules for more like', fakeAsync(() => { + it('should ingest text filter rules for more like', () => { const moreLikeSpy = jest.spyOn(documentService, 'get') moreLikeSpy.mockReturnValue(of({ id: 1, title: 'Foo Bar' })) expect(component.textFilter).toEqual(null) @@ -500,9 +505,9 @@ describe('FilterEditorComponent', () => { value: '1', }, ]) - })) + }) - it('should ingest filter rules for date created after and adjust date by 1 day', fakeAsync(() => { + it('should ingest filter rules for date created after and adjust date by 1 day', () => { expect(component.dateCreatedFrom).toBeNull() component.filterRules = [ { @@ -511,9 +516,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateCreatedFrom).toEqual('2023-05-15') - })) + }) - it('should ingest filter rules for date created from', fakeAsync(() => { + it('should ingest filter rules for date created from', () => { expect(component.dateCreatedFrom).toBeNull() component.filterRules = [ { @@ -522,9 +527,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateCreatedFrom).toEqual('2023-05-14') - })) + }) - it('should ingest filter rules for date created before and adjust date by 1 day', fakeAsync(() => { + it('should ingest filter rules for date created before and adjust date by 1 day', () => { expect(component.dateCreatedTo).toBeNull() component.filterRules = [ { @@ -533,9 +538,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateCreatedTo).toEqual('2023-05-13') - })) + }) - it('should ingest filter rules for date created to', fakeAsync(() => { + it('should ingest filter rules for date created to', () => { expect(component.dateCreatedTo).toBeNull() component.filterRules = [ { @@ -544,9 +549,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateCreatedTo).toEqual('2023-05-14') - })) + }) - it('should ingest filter rules for date added after and adjust date by 1 day', fakeAsync(() => { + it('should ingest filter rules for date added after and adjust date by 1 day', () => { expect(component.dateAddedFrom).toBeNull() component.filterRules = [ { @@ -555,9 +560,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateAddedFrom).toEqual('2023-05-15') - })) + }) - it('should ingest filter rules for date added from', fakeAsync(() => { + it('should ingest filter rules for date added from', () => { expect(component.dateAddedFrom).toBeNull() component.filterRules = [ { @@ -566,9 +571,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateAddedFrom).toEqual('2023-05-14') - })) + }) - it('should ingest filter rules for date added before and adjust date by 1 day', fakeAsync(() => { + it('should ingest filter rules for date added before and adjust date by 1 day', () => { expect(component.dateAddedTo).toBeNull() component.filterRules = [ { @@ -577,9 +582,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateAddedTo).toEqual('2023-05-13') - })) + }) - it('should ingest filter rules for date added to', fakeAsync(() => { + it('should ingest filter rules for date added to', () => { expect(component.dateAddedTo).toBeNull() component.filterRules = [ { @@ -588,9 +593,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.dateAddedTo).toEqual('2023-05-14') - })) + }) - it('should ingest filter rules for has all tags', fakeAsync(() => { + it('should ingest filter rules for has all tags', () => { expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0) component.filterRules = [ { @@ -614,9 +619,9 @@ describe('FilterEditorComponent', () => { }, ] component.toggleTag(2) // coverage - })) + }) - it('should ingest filter rules for has any tags', fakeAsync(() => { + it('should ingest filter rules for has any tags', () => { expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0) component.filterRules = [ { @@ -639,9 +644,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for has any tag', fakeAsync(() => { + it('should ingest filter rules for has any tag', () => { expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(0) component.filterRules = [ { @@ -651,9 +656,9 @@ describe('FilterEditorComponent', () => { ] expect(component.tagSelectionModel.getSelectedItems()).toHaveLength(1) expect(component.tagSelectionModel.get(null)).toBeTruthy() - })) + }) - it('should ingest filter rules for exclude tag(s)', fakeAsync(() => { + it('should ingest filter rules for exclude tag(s)', () => { expect(component.tagSelectionModel.getExcludedItems()).toHaveLength(0) component.filterRules = [ { @@ -676,9 +681,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for has correspondent', fakeAsync(() => { + it('should ingest filter rules for has correspondent', () => { expect( component.correspondentSelectionModel.getSelectedItems() ).toHaveLength(0) @@ -708,9 +713,9 @@ describe('FilterEditorComponent', () => { expect(component.correspondentSelectionModel.getExcludedItems()).toEqual([ { id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' }, ]) - })) + }) - it('should ingest filter rules for has any of correspondents', fakeAsync(() => { + it('should ingest filter rules for has any of correspondents', () => { expect( component.correspondentSelectionModel.getSelectedItems() ).toHaveLength(0) @@ -740,9 +745,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for does not have any of correspondents', fakeAsync(() => { + it('should ingest filter rules for does not have any of correspondents', () => { expect( component.correspondentSelectionModel.getExcludedItems() ).toHaveLength(0) @@ -769,9 +774,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for has document type', fakeAsync(() => { + it('should ingest filter rules for has document type', () => { expect( component.documentTypeSelectionModel.getSelectedItems() ).toHaveLength(0) @@ -801,9 +806,9 @@ describe('FilterEditorComponent', () => { expect(component.documentTypeSelectionModel.getExcludedItems()).toEqual([ { id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' }, ]) - })) + }) - it('should ingest filter rules for has any of document types', fakeAsync(() => { + it('should ingest filter rules for has any of document types', () => { expect( component.documentTypeSelectionModel.getSelectedItems() ).toHaveLength(0) @@ -830,9 +835,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for does not have any of document types', fakeAsync(() => { + it('should ingest filter rules for does not have any of document types', () => { expect( component.documentTypeSelectionModel.getExcludedItems() ).toHaveLength(0) @@ -859,9 +864,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for has storage path', fakeAsync(() => { + it('should ingest filter rules for has storage path', () => { expect(component.storagePathSelectionModel.getSelectedItems()).toHaveLength( 0 ) @@ -891,9 +896,9 @@ describe('FilterEditorComponent', () => { expect(component.storagePathSelectionModel.getExcludedItems()).toEqual([ { id: NEGATIVE_NULL_FILTER_VALUE, name: 'Not assigned' }, ]) - })) + }) - it('should ingest filter rules for has any of storage paths', fakeAsync(() => { + it('should ingest filter rules for has any of storage paths', () => { expect(component.storagePathSelectionModel.getSelectedItems()).toHaveLength( 0 ) @@ -923,9 +928,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for does not have any of storage paths', fakeAsync(() => { + it('should ingest filter rules for does not have any of storage paths', () => { expect(component.storagePathSelectionModel.getExcludedItems()).toHaveLength( 0 ) @@ -952,9 +957,9 @@ describe('FilterEditorComponent', () => { value: null, }, ] - })) + }) - it('should ingest filter rules for custom fields all', fakeAsync(() => { + it('should ingest filter rules for custom fields all', () => { expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy() component.filterRules = [ { @@ -972,9 +977,9 @@ describe('FilterEditorComponent', () => { .value[0] as CustomFieldQueryAtom ).serialize() ).toEqual(['42', CustomFieldQueryOperator.Exists, 'true']) - })) + }) - it('should ingest filter rules for has any custom fields', fakeAsync(() => { + it('should ingest filter rules for has any custom fields', () => { expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy() component.filterRules = [ { @@ -992,9 +997,9 @@ describe('FilterEditorComponent', () => { .value[0] as CustomFieldQueryAtom ).serialize() ).toEqual(['42', CustomFieldQueryOperator.Exists, 'true']) - })) + }) - it('should ingest filter rules for custom field queries', fakeAsync(() => { + it('should ingest filter rules for custom field queries', () => { expect(component.customFieldQueriesModel.isEmpty()).toBeTruthy() component.filterRules = [ { @@ -1027,9 +1032,9 @@ describe('FilterEditorComponent', () => { .value[0] as CustomFieldQueryAtom ).serialize() ).toEqual([42, CustomFieldQueryOperator.Exists, 'true']) - })) + }) - it('should ingest filter rules for owner', fakeAsync(() => { + it('should ingest filter rules for owner', () => { expect(component.permissionsSelectionModel.ownerFilter).toEqual( OwnerFilterType.NONE ) @@ -1044,9 +1049,9 @@ describe('FilterEditorComponent', () => { ) expect(component.permissionsSelectionModel.hideUnowned).toBeFalsy() expect(component.permissionsSelectionModel.userID).toEqual(100) - })) + }) - it('should ingest filter rules for owner is others', fakeAsync(() => { + it('should ingest filter rules for owner is others', () => { expect(component.permissionsSelectionModel.ownerFilter).toEqual( OwnerFilterType.NONE ) @@ -1060,9 +1065,9 @@ describe('FilterEditorComponent', () => { OwnerFilterType.OTHERS ) expect(component.permissionsSelectionModel.includeUsers).toContain(50) - })) + }) - it('should ingest filter rules for owner does not include others', fakeAsync(() => { + it('should ingest filter rules for owner does not include others', () => { expect(component.permissionsSelectionModel.ownerFilter).toEqual( OwnerFilterType.NONE ) @@ -1076,9 +1081,9 @@ describe('FilterEditorComponent', () => { OwnerFilterType.NOT_SELF ) expect(component.permissionsSelectionModel.excludeUsers).toContain(50) - })) + }) - it('should ingest filter rules for owner is null', fakeAsync(() => { + it('should ingest filter rules for owner is null', () => { expect(component.permissionsSelectionModel.ownerFilter).toEqual( OwnerFilterType.NONE ) @@ -1092,9 +1097,9 @@ describe('FilterEditorComponent', () => { OwnerFilterType.UNOWNED ) expect(component.permissionsSelectionModel.hideUnowned).toBeFalsy() - })) + }) - it('should ingest filter rules for owner is not null', fakeAsync(() => { + it('should ingest filter rules for owner is not null', () => { component.filterRules = [ { rule_type: FILTER_OWNER_ISNULL, @@ -1109,9 +1114,9 @@ describe('FilterEditorComponent', () => { }, ] expect(component.permissionsSelectionModel.hideUnowned).toBeTruthy() - })) + }) - it('should ingest filter rules for shared by me', fakeAsync(() => { + it('should ingest filter rules for shared by me', () => { component.filterRules = [ { rule_type: FILTER_SHARED_BY_USER, @@ -1119,11 +1124,11 @@ describe('FilterEditorComponent', () => { }, ] expect(component.permissionsSelectionModel.userID).toEqual(2) - })) + }) // GET filterRules - it('should convert user input to correct filter rules on text field search title + content', fakeAsync(() => { + it('should convert user input to correct filter rules on text field search title + content', () => { component.textFilterInput.nativeElement.value = 'foo' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) fixture.detectChanges() @@ -1135,9 +1140,9 @@ describe('FilterEditorComponent', () => { value: 'foo', }, ]) - })) + }) - it('should convert user input to correct filter rules on text field search title only', fakeAsync(() => { + it('should convert user input to correct filter rules on text field search title only', () => { component.textFilterInput.nativeElement.value = 'foo' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.query( @@ -1154,9 +1159,9 @@ describe('FilterEditorComponent', () => { value: 'foo', }, ]) - })) + }) - it('should convert user input to correct filter rules on text field search equals asn', fakeAsync(() => { + it('should convert user input to correct filter rules on text field search equals asn', () => { component.textFilterInput.nativeElement.value = '1234' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1174,9 +1179,9 @@ describe('FilterEditorComponent', () => { value: '1234', }, ]) - })) + }) - it('should convert user input to correct filter rules on text field search greater than asn', fakeAsync(() => { + it('should convert user input to correct filter rules on text field search greater than asn', () => { component.textFilterInput.nativeElement.value = '123' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1198,9 +1203,9 @@ describe('FilterEditorComponent', () => { value: '123', }, ]) - })) + }) - it('should convert user input to correct filter rules on text field search less than asn', fakeAsync(() => { + it('should convert user input to correct filter rules on text field search less than asn', () => { component.textFilterInput.nativeElement.value = '999' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1222,9 +1227,9 @@ describe('FilterEditorComponent', () => { value: '999', }, ]) - })) + }) - it('should convert user input to correct filter rules on asn is null', fakeAsync(() => { + it('should convert user input to correct filter rules on asn is null', () => { const textFieldTargetDropdown = fixture.debugElement.queryAll( By.directive(NgbDropdownItem) )[2] @@ -1242,9 +1247,9 @@ describe('FilterEditorComponent', () => { value: 'true', }, ]) - })) + }) - it('should convert user input to correct filter rules on asn is not null', fakeAsync(() => { + it('should convert user input to correct filter rules on asn is not null', () => { const textFieldTargetDropdown = fixture.debugElement.queryAll( By.directive(NgbDropdownItem) )[2] @@ -1262,9 +1267,9 @@ describe('FilterEditorComponent', () => { value: 'false', }, ]) - })) + }) - it('should convert user input to correct filter rules on mime type', fakeAsync(() => { + it('should convert user input to correct filter rules on mime type', () => { component.textFilterInput.nativeElement.value = 'pdf' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1280,9 +1285,9 @@ describe('FilterEditorComponent', () => { value: 'pdf', }, ]) - })) + }) - it('should convert user input to correct filter rules on full text query', fakeAsync(() => { + it('should convert user input to correct filter rules on full text query', () => { component.textFilterInput.nativeElement.value = 'foo' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1298,9 +1303,9 @@ describe('FilterEditorComponent', () => { value: 'foo', }, ]) - })) + }) - it('should convert user input to correct filter rules on tag select not assigned', fakeAsync(() => { + it('should convert user input to correct filter rules on tag select not assigned', () => { const tagsFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[0] @@ -1317,9 +1322,9 @@ describe('FilterEditorComponent', () => { value: 'false', }, ]) - })) + }) - it('should convert user input to correct filter rules on tag selections', fakeAsync(() => { + it('should convert user input to correct filter rules on tag selections', () => { const tagsFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[0] // Tags dropdown @@ -1369,9 +1374,9 @@ describe('FilterEditorComponent', () => { value: tags[1].id.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on correspondent selections', fakeAsync(() => { + it('should convert user input to correct filter rules on correspondent selections', () => { const correspondentsFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[1] // Corresp dropdown @@ -1409,9 +1414,9 @@ describe('FilterEditorComponent', () => { value: correspondents[1].id.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on correspondent select not assigned', fakeAsync(() => { + it('should convert user input to correct filter rules on correspondent select not assigned', () => { const correspondentsFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[1] @@ -1441,9 +1446,9 @@ describe('FilterEditorComponent', () => { value: NEGATIVE_NULL_FILTER_VALUE.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on document type selections', fakeAsync(() => { + it('should convert user input to correct filter rules on document type selections', () => { const documentTypesFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[2] // DocType dropdown @@ -1481,9 +1486,9 @@ describe('FilterEditorComponent', () => { value: document_types[1].id.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on doc type select not assigned', fakeAsync(() => { + it('should convert user input to correct filter rules on doc type select not assigned', () => { const docTypesFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[2] @@ -1513,9 +1518,9 @@ describe('FilterEditorComponent', () => { value: NEGATIVE_NULL_FILTER_VALUE.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on storage path selections', fakeAsync(() => { + it('should convert user input to correct filter rules on storage path selections', () => { const storagePathFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[3] // StoragePath dropdown @@ -1553,9 +1558,9 @@ describe('FilterEditorComponent', () => { value: storage_paths[1].id.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on storage path select not assigned', fakeAsync(() => { + it('should convert user input to correct filter rules on storage path select not assigned', () => { const storagePathsFilterableDropdown = fixture.debugElement.queryAll( By.directive(FilterableDropdownComponent) )[3] @@ -1585,9 +1590,9 @@ describe('FilterEditorComponent', () => { value: NEGATIVE_NULL_FILTER_VALUE.toString(), }, ]) - })) + }) - it('should convert user input to correct filter rules on custom field selections', fakeAsync(() => { + it('should convert user input to correct filter rules on custom field selections', () => { const customFieldsQueryDropdown = fixture.debugElement.queryAll( By.directive(CustomFieldsQueryDropdownComponent) )[0] @@ -1617,9 +1622,9 @@ describe('FilterEditorComponent', () => { ]), }, ]) - })) + }) - it('should convert user input to correct filter rules on date created from', fakeAsync(() => { + it('should convert user input to correct filter rules on date created from', () => { const dateCreatedDropdown = fixture.debugElement.queryAll( By.directive(DatesDropdownComponent) )[0] @@ -1637,9 +1642,9 @@ describe('FilterEditorComponent', () => { value: '2023-05-14', }, ]) - })) + }) - it('should convert user input to correct filter rules on date created to', fakeAsync(() => { + it('should convert user input to correct filter rules on date created to', () => { const dateCreatedDropdown = fixture.debugElement.queryAll( By.directive(DatesDropdownComponent) )[0] @@ -1657,9 +1662,9 @@ describe('FilterEditorComponent', () => { value: '2023-05-14', }, ]) - })) + }) - it('should convert user input to correct filter rules on date created with relative date', fakeAsync(() => { + it('should convert user input to correct filter rules on date created with relative date', () => { const dateCreatedDropdown = fixture.debugElement.queryAll( By.directive(DatesDropdownComponent) )[0] @@ -1673,9 +1678,9 @@ describe('FilterEditorComponent', () => { value: 'created:[-1 week to now]', }, ]) - })) + }) - it('should carry over text filtering on date created with relative date', fakeAsync(() => { + it('should carry over text filtering on date created with relative date', () => { component.textFilter = 'foo' const dateCreatedDropdown = fixture.debugElement.queryAll( By.directive(DatesDropdownComponent) @@ -1690,9 +1695,9 @@ describe('FilterEditorComponent', () => { value: 'foo,created:[-1 week to now]', }, ]) - })) + }) - it('should convert legacy title filters into full text query when adding a created relative date', fakeAsync(() => { + it('should convert legacy title filters into full text query when adding a created relative date', () => { component.filterRules = [ { rule_type: FILTER_TITLE, @@ -1712,9 +1717,9 @@ describe('FilterEditorComponent', () => { value: 'foo,created:[-1 week to now]', }, ]) - })) + }) - it('should convert simple title filters into full text query when adding a created relative date', fakeAsync(() => { + it('should convert simple title filters into full text query when adding a created relative date', () => { component.filterRules = [ { rule_type: FILTER_SIMPLE_TITLE, @@ -1734,9 +1739,9 @@ describe('FilterEditorComponent', () => { value: 'foo,created:[-1 week to now]', }, ]) - })) + }) - it('should leave relative dates not in quick list intact', fakeAsync(() => { + it('should leave relative dates not in quick list intact', () => { component.textFilterInput.nativeElement.value = 'created:[-2 week to now]' component.textFilterInput.nativeElement.dispatchEvent(new Event('input')) const textFieldTargetDropdown = fixture.debugElement.queryAll( @@ -1762,9 +1767,9 @@ describe('FilterEditorComponent', () => { value: 'added:[-2 month to now]', }, ]) - })) + }) - it('should convert user input to correct filter rules on date added after', fakeAsync(() => { + it('should convert user input to correct filter rules on date added after', () => { const datesDropdown = fixture.debugElement.query( By.directive(DatesDropdownComponent) ) @@ -1782,9 +1787,9 @@ describe('FilterEditorComponent', () => { value: '2023-05-14', }, ]) - })) + }) - it('should convert user input to correct filter rules on date added before', fakeAsync(() => { + it('should convert user input to correct filter rules on date added before', () => { const datesDropdown = fixture.debugElement.query( By.directive(DatesDropdownComponent) ) @@ -1802,9 +1807,9 @@ describe('FilterEditorComponent', () => { value: '2023-05-14', }, ]) - })) + }) - it('should convert user input to correct filter rules on date added with relative date', fakeAsync(() => { + it('should convert user input to correct filter rules on date added with relative date', () => { const datesDropdown = fixture.debugElement.query( By.directive(DatesDropdownComponent) ) @@ -1818,9 +1823,9 @@ describe('FilterEditorComponent', () => { value: 'added:[-1 week to now]', }, ]) - })) + }) - it('should carry over text filtering on date added with relative date', fakeAsync(() => { + it('should carry over text filtering on date added with relative date', () => { component.textFilter = 'foo' const datesDropdown = fixture.debugElement.query( By.directive(DatesDropdownComponent) @@ -1835,9 +1840,9 @@ describe('FilterEditorComponent', () => { value: 'foo,added:[-1 week to now]', }, ]) - })) + }) - it('should convert user input to correct filter on permissions select my docs', fakeAsync(() => { + it('should convert user input to correct filter on permissions select my docs', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1851,24 +1856,9 @@ describe('FilterEditorComponent', () => { value: '1', }, ]) - })) + }) - it('should convert user input to correct filter on permissions select shared with me', fakeAsync(() => { - const permissionsDropdown = fixture.debugElement.query( - By.directive(PermissionsFilterDropdownComponent) - ) - const sharedWithMe = permissionsDropdown.queryAll(By.css('button'))[3] - sharedWithMe.triggerEventHandler('click') - fixture.detectChanges() - expect(component.filterRules).toEqual([ - { - rule_type: FILTER_OWNER_DOES_NOT_INCLUDE, - value: '1', - }, - ]) - })) - - it('should convert user input to correct filter on permissions select shared with me', fakeAsync(() => { + it('should convert user input to correct filter on permissions select shared with me', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1889,9 +1879,9 @@ describe('FilterEditorComponent', () => { value: '1,2', }, ]) - })) + }) - it('should convert user input to correct filter on permissions select shared by me', fakeAsync(() => { + it('should convert user input to correct filter on permissions select shared by me', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1904,9 +1894,9 @@ describe('FilterEditorComponent', () => { value: '1', }, ]) - })) + }) - it('should convert user input to correct filter on permissions select unowned', fakeAsync(() => { + it('should convert user input to correct filter on permissions select unowned', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1919,9 +1909,9 @@ describe('FilterEditorComponent', () => { value: 'true', }, ]) - })) + }) - it('should convert user input to correct filter on permissions select others', fakeAsync(() => { + it('should convert user input to correct filter on permissions select others', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1940,9 +1930,9 @@ describe('FilterEditorComponent', () => { value: '3', }, ]) - })) + }) - it('should convert user input to correct filter on permissions hide unowned', fakeAsync(() => { + it('should convert user input to correct filter on permissions hide unowned', () => { const permissionsDropdown = fixture.debugElement.query( By.directive(PermissionsFilterDropdownComponent) ) @@ -1960,7 +1950,7 @@ describe('FilterEditorComponent', () => { value: 'false', }, ]) - })) + }) // The rest @@ -2221,7 +2211,7 @@ describe('FilterEditorComponent', () => { }) }) - it('should keep deprecated custom fields target available for legacy filters', fakeAsync(() => { + it('should keep deprecated custom fields target available for legacy filters', () => { component.filterRules = [ { rule_type: FILTER_CUSTOM_FIELDS_TEXT, @@ -2242,9 +2232,9 @@ describe('FilterEditorComponent', () => { value: 'foo', }, ]) - })) + }) - it('should call autocomplete endpoint on input', fakeAsync(() => { + it('should call autocomplete endpoint on input', () => { component.textFilterTarget = 'fulltext-query' // TEXT_FILTER_TARGET_FULLTEXT_QUERY const autocompleteSpy = jest.spyOn(searchService, 'autocomplete') component.searchAutoComplete(of('hello')).subscribe() @@ -2254,9 +2244,9 @@ describe('FilterEditorComponent', () => { component.searchAutoComplete(of('hello world 1')).subscribe() tick(250) expect(autocompleteSpy).toHaveBeenCalled() - })) + }) - it('should handle autocomplete backend failure gracefully', fakeAsync(() => { + it('should handle autocomplete backend failure gracefully', () => { component.textFilterTarget = 'fulltext-query' // TEXT_FILTER_TARGET_FULLTEXT_QUERY const serviceAutocompleteSpy = jest.spyOn(searchService, 'autocomplete') serviceAutocompleteSpy.mockReturnValue( @@ -2270,7 +2260,7 @@ describe('FilterEditorComponent', () => { tick(250) expect(serviceAutocompleteSpy).toHaveBeenCalled() expect(result).toEqual([]) - })) + }) it('should support choosing a autocomplete item', () => { expect(component.textFilter).toBeNull() diff --git a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts index b4e63317a..dc08d17ee 100644 --- a/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts +++ b/src-ui/src/app/components/document-list/filter-editor/filter-editor.component.ts @@ -1166,13 +1166,13 @@ export class FilterEditorComponent private maybeCompleteLoading() { this.loadingCount++ if (this.loadingCount == this.loadingCountTotal) { - this.loading = false - this.show = true + this.loading.set(false) + this.show.set(true) } } ngOnInit() { - this.loading = true + this.loading.set(true) if ( this.permissionsService.currentUserCan( PermissionAction.View, diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html index 4e4bf2cd3..c1058bde2 100644 --- a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html +++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.html @@ -1,24 +1,24 @@

              Save current view

              -
              - + - @if (error?.filter_rules) { + @if (error()?.filter_rules) {
              Filter rules error occurred while saving this view
              The error returned was:
              - {{ error.filter_rules }} + {{ error().filter_rules }}
              }
              - - + +
              diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts index 5726c4255..26f33adaf 100644 --- a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts +++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.spec.ts @@ -1,9 +1,4 @@ -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { By } from '@angular/platform-browser' import { NgbActiveModal, NgbModalModule } from '@ng-bootstrap/ng-bootstrap' @@ -22,8 +17,8 @@ describe('SaveViewConfigDialogComponent', () => { let fixture: ComponentFixture let modal: NgbActiveModal - beforeEach(fakeAsync(() => { - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ providers: [ NgbActiveModal, { @@ -56,16 +51,16 @@ describe('SaveViewConfigDialogComponent', () => { fixture = TestBed.createComponent(SaveViewConfigDialogComponent) component = fixture.componentInstance fixture.detectChanges() - tick() - })) + await fixture.whenStable() + }) it('should support default name', () => { const name = 'Tag: Inbox' let result component.saveClicked.subscribe((saveResult) => (result = saveResult)) - component.defaultName = name + component.setDefaultName(name) component.save() - expect(component.defaultName).toEqual(name) + expect(component.defaultName()).toEqual(name) expect(result).toEqual({ name, showInSideBar: false, diff --git a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts index 78ac67e36..3ac56cc50 100644 --- a/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts +++ b/src-ui/src/app/components/document-list/save-view-config-dialog/save-view-config-dialog.component.ts @@ -1,10 +1,10 @@ import { Component, EventEmitter, - Input, OnInit, Output, inject, + signal, } from '@angular/core' import { FormControl, @@ -32,29 +32,18 @@ import { TextComponent } from '../../common/input/text/text.component' }) export class SaveViewConfigDialogComponent implements OnInit { private modal = inject(NgbActiveModal) + readonly error = signal(undefined) + readonly buttonsEnabled = signal(true) + readonly defaultName = signal('') + readonly closeEnabled = signal(false) @Output() public saveClicked = new EventEmitter() - @Input() - error - - @Input() - buttonsEnabled = true - - closeEnabled = false - users: User[] - _defaultName = '' - - get defaultName() { - return this._defaultName - } - - @Input() - set defaultName(value: string) { - this._defaultName = value + setDefaultName(value: string) { + this.defaultName.set(value) this.saveViewConfigForm.patchValue({ name: value }) } @@ -68,7 +57,7 @@ export class SaveViewConfigDialogComponent implements OnInit { ngOnInit(): void { // wait to enable close button so it doesn't steal focus from input since its the first clickable element in the DOM setTimeout(() => { - this.closeEnabled = true + this.closeEnabled.set(true) }) } diff --git a/src-ui/src/app/components/document-notes/document-notes.component.html b/src-ui/src/app/components/document-notes/document-notes.component.html index cf52b39b3..55f4357ec 100644 --- a/src-ui/src/app/components/document-notes/document-notes.component.html +++ b/src-ui/src/app/components/document-notes/document-notes.component.html @@ -1,4 +1,4 @@ -@if (notes) { +@if (notes()) {
              @@ -8,14 +8,14 @@
              - @if (networkActive) { + @if (networkActive()) {
              } - +

              - @for (note of notes; track note) { + @for (note of notes(); track note) {

              {{note.note}}

              diff --git a/src-ui/src/app/components/document-notes/document-notes.component.spec.ts b/src-ui/src/app/components/document-notes/document-notes.component.spec.ts index 1c86c03a5..85b8c9570 100644 --- a/src-ui/src/app/components/document-notes/document-notes.component.spec.ts +++ b/src-ui/src/app/components/document-notes/document-notes.component.spec.ts @@ -110,7 +110,7 @@ describe('DocumentNotesComponent', () => { }) it('should display notes with user name / username', () => { - component.notes = notes + component.notes.set(notes) fixture.detectChanges() expect(fixture.debugElement.nativeElement.textContent).toContain( notes[0].note @@ -154,7 +154,7 @@ describe('DocumentNotesComponent', () => { }) it('should support note entry, show error if fails', () => { - component.documentId = 12 + component.documentId.set(12) const note = 'This is the new note.' const noteTextArea = fixture.debugElement.query(By.css('textarea')) noteTextArea.nativeElement.value = note @@ -177,7 +177,7 @@ describe('DocumentNotesComponent', () => { }) it('should support note save on ctrl+Enter', () => { - component.documentId = 12 + component.documentId.set(12) const note = 'This is the new note.' const noteTextArea = fixture.debugElement.query(By.css('textarea')) noteTextArea.nativeElement.value = note @@ -189,8 +189,8 @@ describe('DocumentNotesComponent', () => { }) it('should support delete note, show error if fails', () => { - component.documentId = 12 - component.notes = notes + component.documentId.set(12) + component.notes.set(notes) fixture.detectChanges() const deleteButton = fixture.debugElement.queryAll(By.css('button'))[1] // 0 is add button const deleteSpy = jest.spyOn(notesService, 'deleteNote') diff --git a/src-ui/src/app/components/document-notes/document-notes.component.ts b/src-ui/src/app/components/document-notes/document-notes.component.ts index 43530ce7c..19dbdf486 100644 --- a/src-ui/src/app/components/document-notes/document-notes.component.ts +++ b/src-ui/src/app/components/document-notes/document-notes.component.ts @@ -1,4 +1,12 @@ -import { Component, EventEmitter, Input, Output, inject } from '@angular/core' +import { + Component, + EventEmitter, + Output, + inject, + input, + model, + signal, +} from '@angular/core' import { FormControl, FormGroup, @@ -31,23 +39,17 @@ export class DocumentNotesComponent extends ComponentWithPermissions { private notesService = inject(DocumentNotesService) private toastService = inject(ToastService) private usersService = inject(UserService) + readonly documentId = model(undefined) + readonly notes = model([]) + readonly addDisabled = input(false) + readonly networkActive = signal(false) noteForm: FormGroup = new FormGroup({ newNote: new FormControl(''), }) - networkActive = false newNoteError: boolean = false - @Input() - documentId: number - - @Input() - notes: DocumentNote[] = [] - - @Input() - addDisabled: boolean = false - @Output() updated: EventEmitter = new EventEmitter() users: User[] @@ -68,30 +70,30 @@ export class DocumentNotesComponent extends ComponentWithPermissions { return } this.newNoteError = false - this.networkActive = true - this.notesService.addNote(this.documentId, note).subscribe({ + this.networkActive.set(true) + this.notesService.addNote(this.documentId(), note).subscribe({ next: (result) => { - this.notes = result + this.notes.set(result) this.noteForm.get('newNote').reset() - this.networkActive = false - this.updated.emit(this.notes) + this.networkActive.set(false) + this.updated.emit(this.notes()) }, error: (e) => { - this.networkActive = false + this.networkActive.set(false) this.toastService.showError($localize`Error saving note`, e) }, }) } deleteNote(noteId: number) { - this.notesService.deleteNote(this.documentId, noteId).subscribe({ + this.notesService.deleteNote(this.documentId(), noteId).subscribe({ next: (result) => { - this.notes = result - this.networkActive = false - this.updated.emit(this.notes) + this.notes.set(result) + this.networkActive.set(false) + this.updated.emit(this.notes()) }, error: (e) => { - this.networkActive = false + this.networkActive.set(false) this.toastService.showError($localize`Error deleting note`, e) }, }) diff --git a/src-ui/src/app/components/file-drop/file-drop.component.html b/src-ui/src/app/components/file-drop/file-drop.component.html index 71a008b3f..fa03f2522 100644 --- a/src-ui/src/app/components/file-drop/file-drop.component.html +++ b/src-ui/src/app/components/file-drop/file-drop.component.html @@ -1,7 +1,7 @@ -
              +
              -
              +

              Drop files to begin upload

              diff --git a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts b/src-ui/src/app/components/file-drop/file-drop.component.spec.ts index 78e024955..f6ea9572a 100644 --- a/src-ui/src/app/components/file-drop/file-drop.component.spec.ts +++ b/src-ui/src/app/components/file-drop/file-drop.component.spec.ts @@ -1,13 +1,6 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - discardPeriodicTasks, - fakeAsync, - flush, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { By } from '@angular/platform-browser' import { PermissionsService } from 'src/app/services/permissions.service' import { SettingsService } from 'src/app/services/settings.service' @@ -24,8 +17,18 @@ describe('FileDropComponent', () => { let settingsService: SettingsService let uploadDocumentsService: UploadDocumentsService - beforeEach(() => { - TestBed.configureTestingModule({ + const advanceTimers = (ms: number) => { + jest.advanceTimersByTime(ms) + fixture.detectChanges() + } + + const flushPromises = async () => { + await Promise.resolve() + await Promise.resolve() + } + + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [FileDropComponent, ToastsComponent], providers: [ provideHttpClient(withInterceptorsFromDi()), @@ -43,6 +46,10 @@ describe('FileDropComponent', () => { fixture.detectChanges() }) + afterEach(() => { + jest.useRealTimers() + }) + it('should enable drag-drop if user has permissions', () => { jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) expect(component.dragDropEnabled).toBeTruthy() @@ -53,22 +60,21 @@ describe('FileDropComponent', () => { expect(component.dragDropEnabled).toBeFalsy() }) - it('should disable drag-drop if disabled in settings', fakeAsync(() => { + it('should disable drag-drop if disabled in settings', () => { + jest.useFakeTimers() jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = false + settingsService.globalDropzoneEnabled.set(false) expect(component.dragDropEnabled).toBeFalsy() component.onDragOver(new Event('dragover') as DragEvent) - tick(1) - fixture.detectChanges() - expect(component.fileIsOver).toBeFalsy() + advanceTimers(1) + expect(component.fileIsOver()).toBeFalsy() const dropzone = fixture.debugElement.query( By.css('.global-dropzone-overlay') ) expect(dropzone.classes['active']).toBeFalsy() component.onDragLeave(new Event('dragleave') as DragEvent) - tick(700) - fixture.detectChanges() + advanceTimers(700) // drop const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile') const dragEvent = new Event('drop') @@ -79,22 +85,21 @@ describe('FileDropComponent', () => { }, } component.onDrop(dragEvent as DragEvent) - tick(3000) + advanceTimers(3000) expect(uploadSpy).not.toHaveBeenCalled() - })) + }) - it('should support drag drop, initiate upload', fakeAsync(() => { + it('should support drag drop, initiate upload', () => { + jest.useFakeTimers() jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - expect(component.fileIsOver).toBeFalsy() + expect(component.fileIsOver()).toBeFalsy() const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) - tick(1) - fixture.detectChanges() - expect(component.fileIsOver).toBeTruthy() + advanceTimers(1) + expect(component.fileIsOver()).toBeTruthy() component.onDragLeave(new Event('dragleave') as DragEvent) - tick(700) - fixture.detectChanges() + advanceTimers(700) // drop const toastSpy = jest.spyOn(toastService, 'show') const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile') @@ -113,24 +118,22 @@ describe('FileDropComponent', () => { ], } component.onDrop(dragEvent as DragEvent) - tick(3000) + advanceTimers(3000) expect(toastSpy).toHaveBeenCalled() expect(uploadSpy).toHaveBeenCalled() - discardPeriodicTasks() - })) + }) - it('should support drag drop, initiate upload with webkitGetAsEntry', fakeAsync(() => { + it('should support drag drop, initiate upload with webkitGetAsEntry', async () => { + jest.useFakeTimers() jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - expect(component.fileIsOver).toBeFalsy() + expect(component.fileIsOver()).toBeFalsy() const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) - tick(1) - fixture.detectChanges() - expect(component.fileIsOver).toBeTruthy() + advanceTimers(1) + expect(component.fileIsOver()).toBeTruthy() component.onDragLeave(new Event('dragleave') as DragEvent) - tick(700) - fixture.detectChanges() + advanceTimers(700) // drop const toastSpy = jest.spyOn(toastService, 'show') const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile') @@ -154,13 +157,14 @@ describe('FileDropComponent', () => { files: [], } component.onDrop(dragEvent as DragEvent) - tick(3000) + await flushPromises() + advanceTimers(3000) expect(toastSpy).toHaveBeenCalled() expect(uploadSpy).toHaveBeenCalled() - discardPeriodicTasks() - })) + }) - it('should show an error on traverseFileTree error', fakeAsync(() => { + it('should show an error on traverseFileTree error', async () => { + jest.useFakeTimers() jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) const toastSpy = jest.spyOn(toastService, 'showError') const traverseSpy = jest @@ -190,28 +194,25 @@ describe('FileDropComponent', () => { component.onDrop(event) - tick() // flush microtasks (e.g., Promise.reject) + await flushPromises() expect(traverseSpy).toHaveBeenCalled() expect(toastSpy).toHaveBeenCalledWith( $localize`Failed to read dropped items: Error traversing file tree` ) + }) - discardPeriodicTasks() - })) - - it('should support drag drop, initiate upload without DataTransfer API support', fakeAsync(() => { + it('should support drag drop, initiate upload without DataTransfer API support', () => { + jest.useFakeTimers() jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - expect(component.fileIsOver).toBeFalsy() + expect(component.fileIsOver()).toBeFalsy() const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) - tick(1) - fixture.detectChanges() - expect(component.fileIsOver).toBeTruthy() + advanceTimers(1) + expect(component.fileIsOver()).toBeTruthy() component.onDragLeave(new Event('dragleave') as DragEvent) - tick(700) - fixture.detectChanges() + advanceTimers(700) // drop const toastSpy = jest.spyOn(toastService, 'show') const uploadSpy = jest.spyOn(uploadDocumentsService, 'uploadFile') @@ -225,11 +226,10 @@ describe('FileDropComponent', () => { files: [file], } component.onDrop(dragEvent as DragEvent) - tick(3000) + advanceTimers(3000) expect(toastSpy).toHaveBeenCalled() expect(uploadSpy).toHaveBeenCalled() - discardPeriodicTasks() - })) + }) it('should resolve a single file when entry isFile', () => { const mockFile = new File(['data'], 'test.txt', { type: 'text/plain' }) @@ -295,47 +295,47 @@ describe('FileDropComponent', () => { }) }) - it('should ignore events if disabled', fakeAsync(() => { - settingsService.globalDropzoneEnabled = false - expect(settingsService.globalDropzoneActive).toBeFalsy() + it('should ignore events if disabled', () => { + settingsService.globalDropzoneEnabled.set(false) + expect(settingsService.globalDropzoneActive()).toBeFalsy() component.onDragOver(new Event('dragover') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeFalsy() - settingsService.globalDropzoneActive = true + expect(settingsService.globalDropzoneActive()).toBeFalsy() + settingsService.globalDropzoneActive.set(true) component.onDragLeave(new Event('dragleave') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeTruthy() + expect(settingsService.globalDropzoneActive()).toBeTruthy() component.onDrop(new Event('drop') as DragEvent) - expect(settingsService.globalDropzoneActive).toBeTruthy() - })) + expect(settingsService.globalDropzoneActive()).toBeTruthy() + }) - it('should hide if app loses focus', fakeAsync(() => { + it('should hide if app loses focus', () => { + jest.useFakeTimers() const leaveSpy = jest.spyOn(component, 'onDragLeave') jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = true + settingsService.globalDropzoneEnabled.set(true) const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) - tick(1) - expect(component.hidden).toBeFalsy() - expect(component.fileIsOver).toBeTruthy() + advanceTimers(1) + expect(component.hidden()).toBeFalsy() + expect(component.fileIsOver()).toBeTruthy() jest.spyOn(document, 'hidden', 'get').mockReturnValue(true) component.onVisibilityChange() expect(leaveSpy).toHaveBeenCalled() - flush() - })) + }) - it('should hide on window blur', fakeAsync(() => { + it('should hide on window blur', () => { + jest.useFakeTimers() const leaveSpy = jest.spyOn(component, 'onDragLeave') jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) - settingsService.globalDropzoneEnabled = true + settingsService.globalDropzoneEnabled.set(true) const overEvent = new Event('dragover') as DragEvent ;(overEvent as any).dataTransfer = { types: ['Files'] } component.onDragOver(overEvent) - tick(1) - expect(component.hidden).toBeFalsy() - expect(component.fileIsOver).toBeTruthy() + advanceTimers(1) + expect(component.hidden()).toBeFalsy() + expect(component.fileIsOver()).toBeTruthy() jest.spyOn(document, 'hidden', 'get').mockReturnValue(true) component.onWindowBlur() expect(leaveSpy).toHaveBeenCalled() - flush() - })) + }) }) diff --git a/src-ui/src/app/components/file-drop/file-drop.component.ts b/src-ui/src/app/components/file-drop/file-drop.component.ts index 8fd0c7c22..a9a0f4b1d 100644 --- a/src-ui/src/app/components/file-drop/file-drop.component.ts +++ b/src-ui/src/app/components/file-drop/file-drop.component.ts @@ -1,4 +1,4 @@ -import { Component, HostListener, inject } from '@angular/core' +import { Component, HostListener, inject, signal } from '@angular/core' import { PermissionAction, PermissionsService, @@ -21,12 +21,12 @@ export class FileDropComponent { private permissionsService = inject(PermissionsService) private fileLeaveTimeoutID: any - fileIsOver: boolean = false - hidden: boolean = true + fileIsOver = signal(false) + hidden = signal(true) public get dragDropEnabled(): boolean { return ( - this.settings.globalDropzoneEnabled && + this.settings.globalDropzoneEnabled() && this.permissionsService.currentUserCan( PermissionAction.Add, PermissionType.Document @@ -39,12 +39,12 @@ export class FileDropComponent { return event.preventDefault() event.stopImmediatePropagation() - this.settings.globalDropzoneActive = true + this.settings.globalDropzoneActive.set(true) // allows transition setTimeout(() => { - this.fileIsOver = true + this.fileIsOver.set(true) }, 1) - this.hidden = false + this.hidden.set(false) // stop fileLeave timeout clearTimeout(this.fileLeaveTimeoutID) } @@ -56,15 +56,15 @@ export class FileDropComponent { if (!this.dragDropEnabled) return event?.preventDefault() event?.stopImmediatePropagation() - this.settings.globalDropzoneActive = false + this.settings.globalDropzoneActive.set(false) const ms = immediate ? 0 : 500 this.fileLeaveTimeoutID = setTimeout(() => { - this.fileIsOver = false + this.fileIsOver.set(false) // await transition completed setTimeout(() => { - this.hidden = true + this.hidden.set(true) }, 150) }, ms) } diff --git a/src-ui/src/app/components/loading-component/loading.component.spec.ts b/src-ui/src/app/components/loading-component/loading.component.spec.ts index d4667093d..5e898d752 100644 --- a/src-ui/src/app/components/loading-component/loading.component.spec.ts +++ b/src-ui/src/app/components/loading-component/loading.component.spec.ts @@ -25,11 +25,11 @@ describe('LoadingComponentWithPermissions', () => { }) it('should have loading set to true by default', () => { - expect(component.loading).toBeTruthy() + expect(component.loading()).toBeTruthy() }) it('should have show set to false by default', () => { - expect(component.show).toBeFalsy() + expect(component.show()).toBeFalsy() }) it('should call next and complete on unsubscribeNotifier with itself on destroy', () => { diff --git a/src-ui/src/app/components/loading-component/loading.component.ts b/src-ui/src/app/components/loading-component/loading.component.ts index 0cde537d3..c3d2948e4 100644 --- a/src-ui/src/app/components/loading-component/loading.component.ts +++ b/src-ui/src/app/components/loading-component/loading.component.ts @@ -1,4 +1,4 @@ -import { Directive, OnDestroy } from '@angular/core' +import { Directive, OnDestroy, signal } from '@angular/core' import { Subject } from 'rxjs' import { ComponentWithPermissions } from '../with-permissions/with-permissions.component' @@ -7,8 +7,8 @@ export abstract class LoadingComponentWithPermissions extends ComponentWithPermissions implements OnDestroy { - public loading: boolean = true - public show: boolean = false + readonly loading = signal(true) + readonly show = signal(false) protected unsubscribeNotifier: Subject = new Subject() diff --git a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.html b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.html index b90f8d3d1..ed6304ea0 100644 --- a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.html +++ b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.html @@ -8,16 +8,16 @@
            • - @if (loading) { + @if (loading()) {
            • Loading...
            • } - @for (field of fields; track field) { + @for (field of fields(); track field) {
            • -
              +
              {{getDataType(field)}}
              @@ -64,7 +64,7 @@
            • } - @if (!loading && fields.length === 0) { + @if (!loading() && fields().length === 0) {
            • No fields defined.
            • }
            diff --git a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts index 96eaab503..8910b249b 100644 --- a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts +++ b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.spec.ts @@ -94,7 +94,7 @@ describe('CustomFieldsComponent', () => { toastService = TestBed.inject(ToastService) listViewService = TestBed.inject(DocumentListViewService) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 0, username: 'test' } + settingsService.currentUser.set({ id: 0, username: 'test' }) fixture = TestBed.createComponent(CustomFieldsComponent) component = fixture.componentInstance diff --git a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts index 9b9eb98bf..b150ae1da 100644 --- a/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts +++ b/src-ui/src/app/components/manage/document-attributes/custom-fields/custom-fields.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, inject, signal } from '@angular/core' import { RouterModule } from '@angular/router' import { NgbDropdownModule, @@ -6,7 +6,7 @@ import { NgbPaginationModule, } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { delay, takeUntil, tap } from 'rxjs' +import { takeUntil, tap } from 'rxjs' import { ConfirmDialogComponent } from 'src/app/components/common/confirm-dialog/confirm-dialog.component' import { CustomFieldEditDialogComponent } from 'src/app/components/common/edit-dialog/custom-field-edit-dialog/custom-field-edit-dialog.component' import { EditDialogMode } from 'src/app/components/common/edit-dialog/edit-dialog.component' @@ -51,33 +51,33 @@ export class CustomFieldsComponent private readonly documentService = inject(DocumentService) private readonly savedViewService = inject(SavedViewService) - public fields: CustomField[] = [] + readonly fields = signal([]) ngOnInit() { this.reload() } reload() { + this.loading.set(true) this.customFieldsService .listAll() .pipe( takeUntil(this.unsubscribeNotifier), tap((r) => { - this.fields = r.results - }), - delay(100) + this.fields.set(r.results) + }) ) .subscribe(() => { - this.show = true - this.loading = false + this.show.set(true) + this.loading.set(false) }) } editField(field: CustomField) { const modal = this.modalService.open(CustomFieldEditDialogComponent) - modal.componentInstance.dialogMode = field - ? EditDialogMode.EDIT - : EditDialogMode.CREATE + modal.componentInstance.dialogMode.set( + field ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) modal.componentInstance.object = field modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) diff --git a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.html b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.html index 118b61ce3..da6077039 100644 --- a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.html +++ b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.html @@ -59,7 +59,7 @@ } -
              +
                @for (section of visibleSections; track section.id) {
              • diff --git a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.spec.ts b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.spec.ts index 54017a7e0..b0d010e19 100644 --- a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.spec.ts +++ b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.spec.ts @@ -100,7 +100,7 @@ describe('DocumentAttributesComponent', () => { expect(router.navigate).toHaveBeenCalledWith(['attributes', 'tags'], { replaceUrl: true, }) - expect(component.activeNavID).toBe(1) + expect(component.activeNavID()).toBe(1) }) it('should set active section from route param when valid', () => { @@ -116,7 +116,7 @@ describe('DocumentAttributesComponent', () => { fixture.detectChanges() paramMapSubject.next(convertToParamMap({ section: 'customfields' })) - expect(component.activeNavID).toBe(2) + expect(component.activeNavID()).toBe(2) expect(router.navigate).not.toHaveBeenCalled() }) @@ -124,10 +124,10 @@ describe('DocumentAttributesComponent', () => { jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) fixture.detectChanges() - component.activeNavID = 1 + component.activeNavID.set(1) paramMapSubject.next(convertToParamMap({ section: 'customfields' })) - expect(component.activeNavID).toBe(2) + expect(component.activeNavID()).toBe(2) }) it('should redirect to dashboard when no sections are visible', () => { @@ -169,7 +169,7 @@ describe('DocumentAttributesComponent', () => { jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) expect(component.activeManagementList).toBeNull() - component.activeNavID = 1 + component.activeNavID.set(1) expect(component.activeSection.kind).toBe( DocumentAttributesSectionKind.ManagementList ) @@ -180,7 +180,7 @@ describe('DocumentAttributesComponent', () => { jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) expect(component.activeCustomFields).toBeNull() - component.activeNavID = 2 + component.activeNavID.set(2) expect(component.activeSection.kind).toBe( DocumentAttributesSectionKind.CustomFields ) diff --git a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.ts b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.ts index 8396ca6b2..d5e1e3c3a 100644 --- a/src-ui/src/app/components/manage/document-attributes/document-attributes.component.ts +++ b/src-ui/src/app/components/manage/document-attributes/document-attributes.component.ts @@ -1,11 +1,10 @@ import { NgComponentOutlet } from '@angular/common' import { - AfterViewChecked, - ChangeDetectorRef, Component, inject, OnDestroy, OnInit, + signal, Type, ViewChild, } from '@angular/core' @@ -70,13 +69,10 @@ interface DocumentAttributesSection { ClearableBadgeComponent, ], }) -export class DocumentAttributesComponent - implements OnInit, OnDestroy, AfterViewChecked -{ +export class DocumentAttributesComponent implements OnInit, OnDestroy { private readonly permissionsService = inject(PermissionsService) private readonly activatedRoute = inject(ActivatedRoute) private readonly router = inject(Router) - private readonly cdr = inject(ChangeDetectorRef) private readonly unsubscribeNotifier = new Subject() protected readonly PermissionAction = PermissionAction @@ -136,11 +132,12 @@ export class DocumentAttributesComponent ] @ViewChild('activeOutlet', { read: NgComponentOutlet }) - private readonly activeOutlet?: NgComponentOutlet + set activeOutlet(outlet: NgComponentOutlet | undefined) { + this.activeComponent.set(outlet?.componentInstance ?? null) + } - private lastHeaderLoading: boolean - - activeNavID: number = null + readonly activeComponent = signal(null) + readonly activeNavID = signal(null) get visibleSections(): DocumentAttributesSection[] { return this.sections.filter((section) => @@ -153,8 +150,9 @@ export class DocumentAttributesComponent get activeSection(): DocumentAttributesSection | null { return ( - this.visibleSections.find((section) => section.id === this.activeNavID) ?? - null + this.visibleSections.find( + (section) => section.id === this.activeNavID() + ) ?? null ) } @@ -163,14 +161,14 @@ export class DocumentAttributesComponent this.activeSection?.kind !== DocumentAttributesSectionKind.ManagementList ) return null - const instance = this.activeOutlet?.componentInstance + const instance = this.activeComponent() return instance instanceof ManagementListComponent ? instance : null } get activeCustomFields(): CustomFieldsComponent | null { if (this.activeSection?.kind !== DocumentAttributesSectionKind.CustomFields) return null - const instance = this.activeOutlet?.componentInstance + const instance = this.activeComponent() return instance instanceof CustomFieldsComponent ? instance : null } @@ -184,8 +182,8 @@ export class DocumentAttributesComponent get activeHeaderLoading(): boolean { return ( - this.activeManagementList?.loading ?? - this.activeCustomFields?.loading ?? + this.activeManagementList?.loading() ?? + this.activeCustomFields?.loading() ?? false ) } @@ -203,13 +201,13 @@ export class DocumentAttributesComponent return } - if (this.activeNavID !== navIDFromSection) { - this.activeNavID = navIDFromSection + if (this.activeNavID() !== navIDFromSection) { + this.activeNavID.set(navIDFromSection) } if (!section || this.getNavIDForSection(section) == null) { this.router.navigate( - ['attributes', this.getSectionForNavID(this.activeNavID)], + ['attributes', this.getSectionForNavID(this.activeNavID())], { replaceUrl: true } ) } @@ -221,14 +219,6 @@ export class DocumentAttributesComponent this.unsubscribeNotifier.complete() } - ngAfterViewChecked(): void { - const current = this.activeHeaderLoading - if (this.lastHeaderLoading !== current) { - this.lastHeaderLoading = current - this.cdr.detectChanges() - } - } - onNavChange(navChangeEvent: NgbNavChangeEvent): void { const nextSection = this.getSectionForNavID(navChangeEvent.nextId) if (!nextSection) { diff --git a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.html b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.html index f8417a264..0b0a40488 100644 --- a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.html +++ b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.html @@ -19,7 +19,7 @@ per page - + @@ -31,21 +31,21 @@
        - +
        NameMatchingDocument countNameMatchingDocument count{{column.name}}{{column.name}}Actions
        @@ -53,31 +53,31 @@
        -@if (!loading || data.length > 0) { +@if (!loading() || data().length > 0) {
        - @if (displayCollectionSize > 0) { + @if (displayCollectionSize() > 0) {
        - {displayCollectionSize, plural, =1 {One {{typeName}}} other {{{displayCollectionSize || 0}} total {{typeNamePlural}}}} + {displayCollectionSize(), plural, =1 {One {{typeName}}} other {{{displayCollectionSize() || 0}} total {{typeNamePlural}}}} @if (hasSelection) {  ({{selectedCount}} selected) }
        } - @if (collectionSize > 20) { - + @if (collectionSize() > 20) { + }
        } -
        diff --git a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.spec.ts b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.spec.ts index cfc087ba4..7f4ff1dc1 100644 --- a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.spec.ts +++ b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.spec.ts @@ -5,12 +5,7 @@ import { withInterceptorsFromDi, } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' -import { - ComponentFixture, - TestBed, - fakeAsync, - tick, -} from '@angular/core/testing' +import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { By } from '@angular/platform-browser' import { RouterLinkWithHref } from '@angular/router' @@ -140,24 +135,26 @@ describe('ManagementListComponent', () => { // These tests are shared among all management list components - it('should support filtering, clear on Esc key', fakeAsync(() => { + it('should support filtering, clear on Esc key', () => { + jest.useFakeTimers() const nameFilterInput = fixture.debugElement.query(By.css('input')) nameFilterInput.nativeElement.value = 'foo' // nameFilterInput.nativeElement.dispatchEvent(new Event('input')) component.nameFilter = 'foo' // subject normally triggered by ngModel - tick(400) // debounce + jest.advanceTimersByTime(400) // debounce fixture.detectChanges() - expect(component.data).toEqual([tags[0]]) + expect(component.data()).toEqual([tags[0]]) nameFilterInput.nativeElement.dispatchEvent( new KeyboardEvent('keyup', { code: 'Escape' }) ) - tick(400) // debounce + jest.advanceTimersByTime(400) // debounce fixture.detectChanges() expect(component.nameFilter).toBeNull() - expect(component.data).toEqual(tags) - tick(100) // load - })) + expect(component.data()).toEqual(tags) + jest.advanceTimersByTime(100) // load + jest.useRealTimers() + }) it('should support create, show notification on error / success', () => { let modal: NgbModalRef @@ -230,7 +227,8 @@ describe('ManagementListComponent', () => { expect(reloadSpy).toHaveBeenCalled() }) - it('should use API count for pagination and nested ids for displayed total', fakeAsync(() => { + it('should use API count for pagination and nested ids for displayed total', () => { + jest.useFakeTimers() jest.spyOn(tagService, 'listFiltered').mockReturnValueOnce( of({ count: 1, @@ -240,11 +238,12 @@ describe('ManagementListComponent', () => { ) component.reloadData() - tick(100) + jest.advanceTimersByTime(100) - expect(component.collectionSize).toBe(1) - expect(component.displayCollectionSize).toBe(3) - })) + expect(component.collectionSize()).toBe(1) + expect(component.displayCollectionSize()).toBe(3) + jest.useRealTimers() + }) it('should support quick filter for objects', () => { const expectedUrl = documentListViewService.getQuickFilterUrl([ @@ -275,9 +274,9 @@ describe('ManagementListComponent', () => { }) ) ) - component.page = 2 + component.page.set(2) component.reloadData() - expect(component.page).toEqual(1) + expect(component.page()).toEqual(1) }) it('should support toggle select page in vew', () => { @@ -295,28 +294,28 @@ describe('ManagementListComponent', () => { it('selectNone should clear selection and reset toggle flag', () => { component.selectedObjects = new Set([tags[0].id, tags[1].id]) - component.togggleAll = true + component.togggleAll.set(true) component.selectNone() expect(component.selectedObjects.size).toBe(0) - expect(component.togggleAll).toBe(false) + expect(component.togggleAll()).toBe(false) }) it('selectPage should select current page items or clear selection', () => { component.selectPage() expect(component.selectedObjects).toEqual(new Set(tags.map((t) => t.id))) - expect(component.togggleAll).toBe(true) + expect(component.togggleAll()).toBe(true) - component.togggleAll = true + component.togggleAll.set(true) component.clearSelection() expect(component.selectedObjects.size).toBe(0) - expect(component.togggleAll).toBe(false) + expect(component.togggleAll()).toBe(false) }) it('selectAll should activate all-selection mode', () => { ;(tagService.listFiltered as jest.Mock).mockClear() - component.collectionSize = tags.length + component.collectionSize.set(tags.length) component.selectAll() @@ -325,41 +324,41 @@ describe('ManagementListComponent', () => { expect((component as any).allSelectionActive).toBe(true) expect(component.hasSelection).toBe(true) expect(component.selectedCount).toBe(tags.length) - expect(component.togggleAll).toBe(true) + expect(component.togggleAll()).toBe(true) }) it('selectAll should clear selection when collection size is zero', () => { component.selectedObjects = new Set([1]) - component.collectionSize = 0 - component.togggleAll = true + component.collectionSize.set(0) + component.togggleAll.set(true) component.selectAll() expect(component.selectedObjects.size).toBe(0) - expect(component.togggleAll).toBe(false) + expect(component.togggleAll()).toBe(false) }) it('toggleSelected should toggle object selection and update toggle state', () => { component.toggleSelected(tags[0]) expect(component.selectedObjects.has(tags[0].id)).toBe(true) - expect(component.togggleAll).toBe(false) + expect(component.togggleAll()).toBe(false) component.toggleSelected(tags[1]) component.toggleSelected(tags[2]) - expect(component.togggleAll).toBe(true) + expect(component.togggleAll()).toBe(true) component.toggleSelected(tags[1]) expect(component.selectedObjects.has(tags[1].id)).toBe(false) - expect(component.togggleAll).toBe(false) + expect(component.togggleAll()).toBe(false) }) it('areAllPageItemsSelected should return false when page has no selectable items', () => { - component.data = [] + component.data.set([]) component.selectedObjects.clear() expect((component as any).areAllPageItemsSelected()).toBe(false) - component.data = tags + component.data.set(tags) }) it('should support bulk edit permissions', () => { @@ -498,7 +497,7 @@ describe('ManagementListComponent', () => { document_count: 10, parent: 1, } - component['unfilteredData'].push(childTag) + component['unfilteredData'].update((data) => [...data, childTag]) const original = component.getOriginalObject({ id: 4 } as Tag) expect(original).toEqual(childTag) }) @@ -525,7 +524,7 @@ describe('ManagementListComponent', () => { expect(component.pageSize).toBe(25) }) - it('pageSize setter should update settings, reset page and reload data on success', fakeAsync(() => { + it('pageSize setter should update settings, reset page and reload data on success', () => { const reloadSpy = jest.spyOn(component, 'reloadData') const toastErrorSpy = jest.spyOn(toastService, 'showError') @@ -536,21 +535,19 @@ describe('ManagementListComponent', () => { .mockReturnValue(of({ success: true })) component.typeNamePlural = 'tags' - component.page = 2 + component.page.set(2) component.pageSize = 100 - tick() - expect(settingsService.set).toHaveBeenCalledWith( SETTINGS_KEYS.OBJECT_LIST_SIZES, { tags: 100 } ) - expect(component.page).toBe(1) + expect(component.page()).toBe(1) expect(reloadSpy).toHaveBeenCalled() expect(toastErrorSpy).not.toHaveBeenCalled() - })) + }) - it('pageSize setter should show error toast on settings store failure', fakeAsync(() => { + it('pageSize setter should show error toast on settings store failure', () => { const reloadSpy = jest.spyOn(component, 'reloadData') const toastErrorSpy = jest.spyOn(toastService, 'showError') @@ -563,12 +560,10 @@ describe('ManagementListComponent', () => { component.typeNamePlural = 'tags' component.pageSize = 50 - tick() - expect(toastErrorSpy).toHaveBeenCalledWith( 'Error saving settings', expect.any(Error) ) expect(reloadSpy).not.toHaveBeenCalled() - })) + }) }) diff --git a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.ts b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.ts index fd7c1ae11..7156b6214 100644 --- a/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.ts +++ b/src-ui/src/app/components/manage/document-attributes/management-list/management-list.component.ts @@ -5,13 +5,13 @@ import { OnDestroy, OnInit, QueryList, + signal, ViewChildren, } from '@angular/core' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { Subject } from 'rxjs' import { debounceTime, - delay, distinctUntilChanged, takeUntil, tap, @@ -88,25 +88,25 @@ export abstract class ManagementListComponent @ViewChildren(SortableDirective) headers: QueryList - public data: T[] = [] - private unfilteredData: T[] = [] + readonly data = signal([]) + private readonly unfilteredData = signal([]) private currentExtraParams: { [key: string]: any } = null private allSelectionActive = false - public page = 1 + readonly page = signal(1) - public collectionSize = 0 - public displayCollectionSize = 0 + readonly collectionSize = signal(0) + readonly displayCollectionSize = signal(0) - public sortField: string - public sortReverse: boolean + readonly sortField = signal(undefined) + readonly sortReverse = signal(undefined) private nameFilterDebounce: Subject protected unsubscribeNotifier: Subject = new Subject() protected _nameFilter: string public selectedObjects: Set = new Set() - public togggleAll: boolean = false + readonly togggleAll = signal(false) public get hasSelection(): boolean { return this.selectedObjects.size > 0 || this.allSelectionActive @@ -114,7 +114,7 @@ export abstract class ManagementListComponent public get selectedCount(): number { return this.allSelectionActive - ? this.displayCollectionSize + ? this.displayCollectionSize() : this.selectedObjects.size } @@ -131,7 +131,7 @@ export abstract class ManagementListComponent ) .subscribe((title) => { this._nameFilter = title - this.page = 1 + this.page.set(1) this.reloadData() }) } @@ -151,8 +151,8 @@ export abstract class ManagementListComponent } onSort(event: SortEvent) { - this.sortField = event.column - this.sortReverse = event.reverse + this.sortField.set(event.column) + this.sortReverse.set(event.reverse) this.reloadData() } @@ -171,25 +171,25 @@ export abstract class ManagementListComponent getDocumentCount(object: MatchingModel): number { return ( object.document_count ?? - this.unfilteredData.find((d) => d.id == object.id)?.document_count ?? + this.unfilteredData().find((d) => d.id == object.id)?.document_count ?? 0 ) } public getOriginalObject(object: T): T { - return this.unfilteredData.find((d) => d?.id == object?.id) || object + return this.unfilteredData().find((d) => d?.id == object?.id) || object } reloadData(extraParams: { [key: string]: any } = null) { - this.loading = true + this.loading.set(true) this.currentExtraParams = extraParams this.clearSelection() this.service .listFiltered( - this.page, + this.page(), this.pageSize, - this.sortField, - this.sortReverse, + this.sortField(), + this.sortReverse(), this._nameFilter, true, extraParams @@ -197,23 +197,22 @@ export abstract class ManagementListComponent .pipe( takeUntil(this.unsubscribeNotifier), tap((c) => { - this.unfilteredData = c.results - this.data = this.filterData(c.results) - this.collectionSize = this.getCollectionSize(c) - this.displayCollectionSize = this.getDisplayCollectionSize(c) - }), - delay(100) + this.unfilteredData.set(c.results) + this.data.set(this.filterData(c.results)) + this.collectionSize.set(this.getCollectionSize(c)) + this.displayCollectionSize.set(this.getDisplayCollectionSize(c)) + }) ) .subscribe({ error: (error: HttpErrorResponse) => { if (error.error?.detail?.includes('Invalid page')) { - this.page = 1 + this.page.set(1) this.reloadData() } }, next: () => { - this.show = true - this.loading = false + this.show.set(true) + this.loading.set(false) }, }) } @@ -222,7 +221,7 @@ export abstract class ManagementListComponent const activeModal = this.modalService.open(this.editDialogComponent, { backdrop: 'static', }) - activeModal.componentInstance.dialogMode = EditDialogMode.CREATE + activeModal.componentInstance.dialogMode.set(EditDialogMode.CREATE) activeModal.componentInstance.succeeded.subscribe(() => { this.reloadData() this.toastService.showInfo( @@ -242,7 +241,7 @@ export abstract class ManagementListComponent backdrop: 'static', }) activeModal.componentInstance.object = object - activeModal.componentInstance.dialogMode = EditDialogMode.EDIT + activeModal.componentInstance.dialogMode.set(EditDialogMode.EDIT) activeModal.componentInstance.succeeded.subscribe(() => { this.reloadData() this.toastService.showInfo( @@ -322,7 +321,7 @@ export abstract class ManagementListComponent }) this.settingsService.storeSettings().subscribe({ next: () => { - this.page = 1 + this.page.set(1) this.reloadData() }, error: (error) => { @@ -346,7 +345,7 @@ export abstract class ManagementListComponent if (!this.permissionsService.currentUserCan(action, this.permissionType)) return false let ownsAll: boolean = true - const objects = this.data.filter((o) => this.selectedObjects.has(o.id)) + const objects = this.data().filter((o) => this.selectedObjects.has(o.id)) ownsAll = objects.every((o) => this.permissionsService.currentUserOwnsObject(o) ) @@ -367,7 +366,7 @@ export abstract class ManagementListComponent clearSelection() { this.allSelectionActive = false - this.togggleAll = false + this.togggleAll.set(false) this.selectedObjects.clear() } @@ -377,19 +376,19 @@ export abstract class ManagementListComponent selectPage() { this.allSelectionActive = false - this.selectedObjects = new Set(this.getSelectableIDs(this.data)) - this.togggleAll = this.areAllPageItemsSelected() + this.selectedObjects = new Set(this.getSelectableIDs(this.data())) + this.togggleAll.set(this.areAllPageItemsSelected()) } selectAll() { - if (!this.collectionSize) { + if (!this.collectionSize()) { this.clearSelection() return } this.allSelectionActive = true - this.selectedObjects = new Set(this.getSelectableIDs(this.data)) - this.togggleAll = this.areAllPageItemsSelected() + this.selectedObjects = new Set(this.getSelectableIDs(this.data())) + this.togggleAll.set(this.areAllPageItemsSelected()) } toggleSelected(object) { @@ -399,14 +398,14 @@ export abstract class ManagementListComponent this.selectedObjects.has(object.id) ? this.selectedObjects.delete(object.id) : this.selectedObjects.add(object.id) - this.togggleAll = this.areAllPageItemsSelected() + this.togggleAll.set(this.areAllPageItemsSelected()) } protected areAllPageItemsSelected(): boolean { if (this.allSelectionActive) { - return this.data.length > 0 + return this.data().length > 0 } - const ids = this.getSelectableIDs(this.data) + const ids = this.getSelectableIDs(this.data()) return ids.length > 0 && ids.every((id) => this.selectedObjects.has(id)) } @@ -416,7 +415,7 @@ export abstract class ManagementListComponent }) modal.componentInstance.confirmClicked.subscribe( ({ permissions, merge }) => { - modal.componentInstance.buttonsEnabled = false + modal.componentInstance.buttonsEnabled.set(false) this.service .bulk_edit_objects( this.allSelectionActive ? [] : Array.from(this.selectedObjects), @@ -435,7 +434,7 @@ export abstract class ManagementListComponent this.reloadData() }, error: (error) => { - modal.componentInstance.buttonsEnabled = true + modal.componentInstance.buttonsEnabled.set(true) this.toastService.showError( $localize`Error updating permissions`, error diff --git a/src-ui/src/app/components/manage/document-attributes/management-list/tag-list/tag-list.component.spec.ts b/src-ui/src/app/components/manage/document-attributes/management-list/tag-list/tag-list.component.spec.ts index dab6a5809..d30408a22 100644 --- a/src-ui/src/app/components/manage/document-attributes/management-list/tag-list/tag-list.component.spec.ts +++ b/src-ui/src/app/components/manage/document-attributes/management-list/tag-list/tag-list.component.spec.ts @@ -136,7 +136,7 @@ describe('TagListComponent', () => { ], } - component.data = [parent as any] + component.data.set([parent as any]) component.selectPage() expect(component.selectedObjects.has(10)).toBe(true) diff --git a/src-ui/src/app/components/manage/mail/mail.component.html b/src-ui/src/app/components/manage/mail/mail.component.html index 204bc9e1c..c0eb0a346 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.html +++ b/src-ui/src/app/components/manage/mail/mail.component.html @@ -34,16 +34,16 @@
        - @if (loadingAccounts) { + @if (loadingAccounts()) {
      • Loading...
      • } - @for (account of mailAccounts; track account) { + @for (account of mailAccounts(); track account) {
      • -
        +
      • } - @if (!loadingAccounts && mailAccounts.length === 0) { + @if (!loadingAccounts() && mailAccounts().length === 0) {
      • No mail accounts defined.
      • } @@ -117,19 +117,19 @@ - @if (loadingRules) { + @if (loadingRules()) {
      • Loading...
      • } - @for (rule of mailRules; track rule) { + @for (rule of mailRules(); track rule) {
      • -
        +
        {{rule.order}}
        -
        {{ mailAccountsById.get(rule.account)?.name }}
        +
        {{ mailAccountsById().get(rule.account)?.name }}
        @@ -179,14 +179,14 @@
      • } - @if (!loadingRules && mailRules.length === 0) { + @if (!loadingRules() && mailRules().length === 0) {
      • No mail rules defined.
      • } -@if (!mailAccounts || !mailRules) { +@if (!mailAccounts() || !mailRules()) {
        Loading...
        diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index c866160d4..cb2111fcd 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -113,7 +113,7 @@ describe('MailComponent', () => { permissionsService = TestBed.inject(PermissionsService) activatedRoute = TestBed.inject(ActivatedRoute) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { id: 1 } + settingsService.currentUser.set({ id: 1 }) jest.spyOn(permissionsService, 'currentUserCan').mockReturnValue(true) jest .spyOn(permissionsService, 'currentUserHasObjectPermissions') @@ -261,7 +261,7 @@ describe('MailComponent', () => { const editDialog = modal.componentInstance as MailRuleEditDialogComponent expect(editDialog.object.id).toBeNull() expect(editDialog.object.name).toEqual(`${mailRules[0].name} (copy)`) - expect(editDialog.dialogMode).toEqual(EditDialogMode.CREATE) + expect(editDialog.dialogMode()).toEqual(EditDialogMode.CREATE) }) it('should support delete mail rule, show error if needed', () => { @@ -414,6 +414,6 @@ describe('MailComponent', () => { modalService.activeInstances.subscribe((refs) => (modal = refs[0])) component.viewProcessedMail(mailRules[0] as MailRule) const dialog = modal.componentInstance as any - expect(dialog.rule).toEqual(mailRules[0]) + expect(dialog.rule()).toEqual(mailRules[0]) }) }) diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index bc119f525..f8be041a3 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -1,9 +1,9 @@ -import { Component, OnDestroy, OnInit, inject } from '@angular/core' +import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { ActivatedRoute } from '@angular/router' import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { Subject, delay, first, takeUntil, tap } from 'rxjs' +import { Subject, first, takeUntil, tap } from 'rxjs' import { MailAccount, MailAccountType } from 'src/app/data/mail-account' import { MailRule } from 'src/app/data/mail-rule' import { ObjectWithPermissions } from 'src/app/data/object-with-permissions' @@ -56,19 +56,17 @@ export class MailComponent public MailAccountType = MailAccountType - private _mailAccounts: MailAccount[] = [] + readonly mailAccounts = signal([]) - public get mailAccounts() { - return this._mailAccounts - } - private set mailAccounts(accounts: MailAccount[]) { - this._mailAccounts = accounts - this.mailAccountsById = new Map( - accounts.map((account) => [account.id, account]) + private setMailAccounts(accounts: MailAccount[]) { + this.mailAccounts.set(accounts) + this.mailAccountsById.set( + new Map(accounts.map((account) => [account.id, account])) ) } - public mailAccountsById: Map = new Map() - public mailRules: MailRule[] = [] + readonly mailAccountsById = signal>(new Map()) + + readonly mailRules = signal([]) unsubscribeNotifier: Subject = new Subject() oAuthAccountId: number @@ -81,10 +79,10 @@ export class MailComponent return this.settingsService.get(SETTINGS_KEYS.OUTLOOK_OAUTH_URL) } - public loadingRules: boolean = true - public showRules: boolean = false - public loadingAccounts: boolean = true - public showAccounts: boolean = false + readonly loadingRules = signal(true) + readonly showRules = signal(false) + readonly loadingAccounts = signal(true) + readonly showAccounts = signal(false) ngOnInit(): void { this.mailAccountService @@ -93,23 +91,21 @@ export class MailComponent first(), takeUntil(this.unsubscribeNotifier), tap((r) => { - this.mailAccounts = r.results + this.setMailAccounts(r.results) + this.loadingAccounts.set(false) + this.showAccounts.set(true) if (this.oAuthAccountId) { this.editMailAccount( - this.mailAccounts.find( + this.mailAccounts().find( (account) => account.id === this.oAuthAccountId ) ) } - }), - delay(100) + }) ) .subscribe({ - next: () => { - this.loadingAccounts = false - this.showAccounts = true - }, error: (e) => { + this.loadingAccounts.set(false) this.toastService.showError( $localize`Error retrieving mail accounts`, e @@ -123,16 +119,14 @@ export class MailComponent first(), takeUntil(this.unsubscribeNotifier), tap((r) => { - this.mailRules = r.results - }), - delay(100) + this.mailRules.set(r.results) + this.loadingRules.set(false) + this.showRules.set(true) + }) ) .subscribe({ - next: (r) => { - this.loadingRules = false - this.showRules = true - }, error: (e) => { + this.loadingRules.set(false) this.toastService.showError($localize`Error retrieving mail rules`, e) }, }) @@ -143,9 +137,9 @@ export class MailComponent if (success) { this.toastService.showInfo($localize`OAuth2 authentication success`) this.oAuthAccountId = parseInt(params.get('account_id')) - if (this.mailAccounts.length > 0) { + if (this.mailAccounts().length > 0) { this.editMailAccount( - this.mailAccounts.find( + this.mailAccounts().find( (account) => account.id === this.oAuthAccountId ) ) @@ -168,9 +162,9 @@ export class MailComponent backdrop: 'static', size: 'xl', }) - modal.componentInstance.dialogMode = account - ? EditDialogMode.EDIT - : EditDialogMode.CREATE + modal.componentInstance.dialogMode.set( + account ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) modal.componentInstance.object = account modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) @@ -182,7 +176,7 @@ export class MailComponent this.mailAccountService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailAccounts = r.results + this.setMailAccounts(r.results) }) }) modal.componentInstance.failed @@ -213,7 +207,7 @@ export class MailComponent this.mailAccountService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailAccounts = r.results + this.setMailAccounts(r.results) }) }, error: (e) => { @@ -247,8 +241,9 @@ export class MailComponent backdrop: 'static', size: 'xl', }) - modal.componentInstance.dialogMode = + modal.componentInstance.dialogMode.set( rule && !forceCreate ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) modal.componentInstance.object = rule modal.componentInstance.succeeded .pipe(takeUntil(this.unsubscribeNotifier)) @@ -258,7 +253,7 @@ export class MailComponent this.mailRuleService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailRules = r.results + this.mailRules.set(r.results) }) }) modal.componentInstance.failed @@ -314,7 +309,7 @@ export class MailComponent this.mailRuleService .listAll(null, null, { full_perms: true }) .subscribe((r) => { - this.mailRules = r.results + this.mailRules.set(r.results) }) }, error: (e) => { @@ -336,7 +331,7 @@ export class MailComponent dialog.object = object modal.componentInstance.confirmClicked.subscribe( ({ permissions, merge }) => { - modal.componentInstance.buttonsEnabled = false + modal.componentInstance.buttonsEnabled.set(false) const service: AbstractPaperlessService = 'account' in object ? this.mailRuleService : this.mailAccountService object.owner = permissions['owner'] @@ -362,7 +357,7 @@ export class MailComponent backdrop: 'static', size: 'xl', }) - modal.componentInstance.rule = rule + modal.componentInstance.rule.set(rule) } userCanEdit(obj: ObjectWithPermissions): boolean { diff --git a/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.html b/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.html index 26b63fd56..675b7284d 100644 --- a/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.html +++ b/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.html @@ -1,5 +1,5 @@
        -
        Processed Mail for {{ rule.name }}
        +
        Processed Mail for {{ rule().name }}
        @@ -10,13 +10,13 @@
        - @if (loading) { + @if (loading()) {
        Loading...
        - } @else if (processedMails.length === 0) { + } @else if (processedMails().length === 0) { No processed email messages found. } @else {
        @@ -25,7 +25,7 @@
        - +
        - +
        - +
        { toastService = TestBed.inject(ToastService) fixture = TestBed.createComponent(ProcessedMailDialogComponent) component = fixture.componentInstance - component.rule = rule + component.rule.set(rule) }) afterEach(() => { @@ -83,8 +83,8 @@ describe('ProcessedMailDialogComponent', () => { fixture.detectChanges() const req = expectListRequest(rule.id) req.flush({ count: 2, results: mails }) - expect(component.loading).toBeFalsy() - expect(component.processedMails).toEqual(mails) + expect(component.loading()).toBeFalsy() + expect(component.processedMails()).toEqual(mails) }) it('should delete selected mails and reload', () => { @@ -94,8 +94,8 @@ describe('ProcessedMailDialogComponent', () => { initialReq.flush({ count: 0, results: [] }) // select a couple of mails and delete - component.selectedMailIds.add(5) - component.selectedMailIds.add(6) + component.selectedMailIds().add(5) + component.selectedMailIds().add(6) const toastInfoSpy = jest.spyOn(toastService, 'showInfo') component.deleteSelected() @@ -127,18 +127,18 @@ describe('ProcessedMailDialogComponent', () => { header.dispatchEvent(new Event('click')) header.checked = true header.dispatchEvent(new Event('click')) - expect(component.selectedMailIds.size).toEqual(mails.length) + expect(component.selectedMailIds().size).toEqual(mails.length) // toggle a single mail component.toggleSelected(mails[0] as any) - expect(component.selectedMailIds.has(mails[0].id)).toBeFalsy() + expect(component.selectedMailIds().has(mails[0].id)).toBeFalsy() component.toggleSelected(mails[0] as any) - expect(component.selectedMailIds.has(mails[0].id)).toBeTruthy() + expect(component.selectedMailIds().has(mails[0].id)).toBeTruthy() // clear selection component.clearSelection() - expect(component.selectedMailIds.size).toEqual(0) - expect(component.toggleAllEnabled).toBeFalsy() + expect(component.selectedMailIds().size).toEqual(0) + expect(component.toggleAllEnabled()).toBeFalsy() }) it('should close the dialog', () => { diff --git a/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.ts b/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.ts index ed51ad0ed..32db2eb26 100644 --- a/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.ts +++ b/src-ui/src/app/components/manage/mail/processed-mail-dialog/processed-mail-dialog.component.ts @@ -1,5 +1,5 @@ import { SlicePipe } from '@angular/common' -import { Component, inject, Input, OnInit } from '@angular/core' +import { Component, inject, OnInit, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbActiveModal, @@ -36,16 +36,14 @@ export class ProcessedMailDialogComponent implements OnInit { private readonly processedMailService = inject(ProcessedMailService) private readonly toastService = inject(ToastService) - public processedMails: ProcessedMail[] = [] - - public loading: boolean = true - public toggleAllEnabled: boolean = false - public readonly selectedMailIds: Set = new Set() + readonly rule = signal(undefined) + readonly processedMails = signal([]) + readonly loading = signal(true) + readonly toggleAllEnabled = signal(false) + readonly selectedMailIds = signal>(new Set()) public page: number = 1 - @Input() rule: MailRule - ngOnInit(): void { this.loadProcessedMails() } @@ -55,19 +53,19 @@ export class ProcessedMailDialogComponent implements OnInit { } private loadProcessedMails(): void { - this.loading = true + this.loading.set(true) this.clearSelection() this.processedMailService - .list(this.page, 50, 'processed_at', true, { rule: this.rule.id }) + .list(this.page, 50, 'processed_at', true, { rule: this.rule().id }) .subscribe((result) => { - this.processedMails = result.results - this.loading = false + this.processedMails.set(result.results) + this.loading.set(false) }) } public deleteSelected(): void { this.processedMailService - .bulk_delete(Array.from(this.selectedMailIds)) + .bulk_delete(Array.from(this.selectedMailIds())) .subscribe(() => { this.toastService.showInfo($localize`Processed mail(s) deleted`) this.loadProcessedMails() @@ -75,22 +73,25 @@ export class ProcessedMailDialogComponent implements OnInit { } public toggleAll(event: PointerEvent) { + const selectedMailIds = new Set() if ((event.target as HTMLInputElement).checked) { - this.selectedMailIds.clear() - this.processedMails.forEach((mail) => this.selectedMailIds.add(mail.id)) + this.processedMails().forEach((mail) => selectedMailIds.add(mail.id)) + this.selectedMailIds.set(selectedMailIds) } else { this.clearSelection() } } public clearSelection() { - this.toggleAllEnabled = false - this.selectedMailIds.clear() + this.toggleAllEnabled.set(false) + this.selectedMailIds.set(new Set()) } public toggleSelected(mail: ProcessedMail) { - this.selectedMailIds.has(mail.id) - ? this.selectedMailIds.delete(mail.id) - : this.selectedMailIds.add(mail.id) + const selectedMailIds = new Set(this.selectedMailIds()) + selectedMailIds.has(mail.id) + ? selectedMailIds.delete(mail.id) + : selectedMailIds.add(mail.id) + this.selectedMailIds.set(selectedMailIds) } } diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.html b/src-ui/src/app/components/manage/saved-views/saved-views.component.html index 1c14b6832..c4005cb2d 100644 --- a/src-ui/src/app/components/manage/saved-views/saved-views.component.html +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.html @@ -7,7 +7,7 @@
          - @for (view of savedViews; track view) { + @for (view of savedViews(); track view) {
        • @@ -65,13 +65,13 @@
        • } - @if (savedViews && savedViews.length === 0) { + @if (savedViews() && savedViews().length === 0) {
        • No saved views defined.
        • } - @if (loading) { + @if (loading()) {
        • Loading...
          diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts b/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts index de6c88f9a..735e5abf4 100644 --- a/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.spec.ts @@ -1,6 +1,7 @@ import { DragDropModule } from '@angular/cdk/drag-drop' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { provideHttpClientTesting } from '@angular/common/http/testing' +import { signal } from '@angular/core' import { ComponentFixture, TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' @@ -226,7 +227,8 @@ describe('SavedViewsComponent', () => { const modalRef = { componentInstance: { confirmClicked, - buttonsEnabled: true, + buttonsEnabled: signal(true), + note: signal(null), }, close: jest.fn(), } as any diff --git a/src-ui/src/app/components/manage/saved-views/saved-views.component.ts b/src-ui/src/app/components/manage/saved-views/saved-views.component.ts index 9a0f11ea7..3d9ee84dd 100644 --- a/src-ui/src/app/components/manage/saved-views/saved-views.component.ts +++ b/src-ui/src/app/components/manage/saved-views/saved-views.component.ts @@ -1,5 +1,5 @@ import { AsyncPipe } from '@angular/common' -import { Component, OnDestroy, OnInit, inject } from '@angular/core' +import { Component, OnDestroy, OnInit, inject, signal } from '@angular/core' import { FormControl, FormGroup, @@ -56,7 +56,7 @@ export class SavedViewsComponent DisplayMode = DisplayMode - public savedViews: SavedView[] + readonly savedViews = signal(undefined) private savedViewsGroup = new FormGroup({}) public savedViewsForm: FormGroup = new FormGroup({ savedViews: this.savedViewsGroup, @@ -66,12 +66,12 @@ export class SavedViewsComponent public isDirty$: Observable get displayFields() { - return this.settings.allDisplayFields + return this.settings.allDisplayFields() } constructor() { super() - this.settings.organizingSidebarSavedViews = true + this.settings.organizingSidebarSavedViews.set(true) } ngOnInit(): void { @@ -79,29 +79,29 @@ export class SavedViewsComponent } private reloadViews(): void { - this.loading = true + this.loading.set(true) this.savedViewService .list(null, null, null, false, { full_perms: true }) .subscribe((r) => { - this.savedViews = r.results + this.savedViews.set(r.results) this.initialize() }) } ngOnDestroy(): void { - this.settings.organizingSidebarSavedViews = false + this.settings.organizingSidebarSavedViews.set(false) super.ngOnDestroy() } private initialize() { - this.loading = false + this.loading.set(false) this.emptyGroup(this.savedViewsGroup) let storeData = { savedViews: {}, } - for (let view of this.savedViews) { + for (let view of this.savedViews()) { storeData.savedViews[view.id.toString()] = { id: view.id, name: view.name, @@ -148,7 +148,9 @@ export class SavedViewsComponent public deleteSavedView(savedView: SavedView) { this.savedViewService.delete(savedView).subscribe(() => { this.savedViewsGroup.removeControl(savedView.id.toString()) - this.savedViews.splice(this.savedViews.indexOf(savedView), 1) + this.savedViews.update((savedViews) => + savedViews.filter((view) => view !== savedView) + ) this.toastService.showInfo( $localize`Saved view "${savedView.name}" deleted.` ) @@ -254,10 +256,12 @@ export class SavedViewsComponent }) const dialog = modal.componentInstance as PermissionsDialogComponent dialog.object = savedView - dialog.note = $localize`Note: Sharing saved views does not share the underlying documents.` + dialog.note.set( + $localize`Note: Sharing saved views does not share the underlying documents.` + ) modal.componentInstance.confirmClicked.subscribe(({ permissions }) => { - modal.componentInstance.buttonsEnabled = false + modal.componentInstance.buttonsEnabled.set(false) const view = { id: savedView.id, owner: permissions.owner, diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.html b/src-ui/src/app/components/manage/workflows/workflows.component.html index 1c03d871b..4849dbc6f 100644 --- a/src-ui/src/app/components/manage/workflows/workflows.component.html +++ b/src-ui/src/app/components/manage/workflows/workflows.component.html @@ -22,16 +22,16 @@
      • - @if (loading && workflows.length === 0) { + @if (loading() && workflows().length === 0) {
      • Loading...
      • } - @for (workflow of workflows; track workflow.id) { + @for (workflow of workflows(); track workflow.id) {
      • -
        +
        {{workflow.order}}
        @@ -76,7 +76,7 @@
      • } - @if (!loading && workflows.length === 0) { -
      • No workflows defined.
      • + @if (!loading() && workflows().length === 0) { +
      • No workflows defined.
      • }
      diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts b/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts index 636e03d54..c9e5e6ff8 100644 --- a/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts +++ b/src-ui/src/app/components/manage/workflows/workflows.component.spec.ts @@ -183,7 +183,7 @@ describe('WorkflowsComponent', () => { expect(modal).not.toBeUndefined() const editDialog = modal.componentInstance as WorkflowEditDialogComponent expect(editDialog.object.name).toEqual(workflows[0].name + ' (copy)') - expect(editDialog.dialogMode).toEqual(EditDialogMode.CREATE) + expect(editDialog.dialogMode()).toEqual(EditDialogMode.CREATE) }) it('should null ids on copy', () => { diff --git a/src-ui/src/app/components/manage/workflows/workflows.component.ts b/src-ui/src/app/components/manage/workflows/workflows.component.ts index 710eb3aa3..554dfe91b 100644 --- a/src-ui/src/app/components/manage/workflows/workflows.component.ts +++ b/src-ui/src/app/components/manage/workflows/workflows.component.ts @@ -1,8 +1,8 @@ -import { Component, OnInit, inject } from '@angular/core' +import { Component, OnInit, inject, signal } from '@angular/core' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { NgbDropdownModule, NgbModal } from '@ng-bootstrap/ng-bootstrap' import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons' -import { delay, takeUntil, tap } from 'rxjs' +import { takeUntil, tap } from 'rxjs' import { Workflow } from 'src/app/data/workflow' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsService } from 'src/app/services/permissions.service' @@ -39,24 +39,23 @@ export class WorkflowsComponent private modalService = inject(NgbModal) private toastService = inject(ToastService) - public workflows: Workflow[] = [] + readonly workflows = signal([]) ngOnInit() { this.reload() } reload() { - this.loading = true + this.loading.set(true) this.workflowService .listAll() .pipe( takeUntil(this.unsubscribeNotifier), - tap((r) => (this.workflows = r.results)), - delay(100) + tap((r) => this.workflows.set(r.results)) ) .subscribe(() => { - this.show = true - this.loading = false + this.show.set(true) + this.loading.set(false) }) } @@ -74,8 +73,9 @@ export class WorkflowsComponent backdrop: 'static', size: 'xl', }) - modal.componentInstance.dialogMode = + modal.componentInstance.dialogMode.set( workflow && !forceCreate ? EditDialogMode.EDIT : EditDialogMode.CREATE + ) if (workflow) { // quick "deep" clone so original doesn't get modified const clone = Object.assign({}, workflow) diff --git a/src-ui/src/app/services/document-list-view.service.spec.ts b/src-ui/src/app/services/document-list-view.service.spec.ts index 30cb8c701..9447101ad 100644 --- a/src-ui/src/app/services/document-list-view.service.spec.ts +++ b/src-ui/src/app/services/document-list-view.service.spec.ts @@ -109,6 +109,7 @@ describe('DocumentListViewService', () => { documentListViewService = TestBed.inject(DocumentListViewService) settingsService = TestBed.inject(SettingsService) router = TestBed.inject(Router) + jest.spyOn(router, 'navigate').mockResolvedValue(true) }) afterEach(() => { @@ -705,11 +706,16 @@ describe('DocumentListViewService', () => { const customFields = ['custom_field_1', 'custom_field_2'] documentListViewService.displayFields = customFields as any expect(documentListViewService.displayFields).toEqual(customFields) - jest.spyOn(settingsService, 'allDisplayFields', 'get').mockReturnValue([ + const mockDisplayFields = [ { id: DisplayField.ADDED, name: 'Added' }, { id: DisplayField.TITLE, name: 'Title' }, { id: 'custom_field_1', name: 'Custom Field 1' }, - ] as any) + ] as any + jest + .spyOn(settingsService.allDisplayFields, 'toString') + .mockReturnValue(mockDisplayFields.toString() as any) + // Mock the signal directly by overriding its value + settingsService.allDisplayFields = jest.fn(() => mockDisplayFields) as any settingsService.displayFieldsInit.emit(true) expect(documentListViewService.displayFields).toEqual(['custom_field_1']) diff --git a/src-ui/src/app/services/document-list-view.service.ts b/src-ui/src/app/services/document-list-view.service.ts index 1d37000bf..de1db2be6 100644 --- a/src-ui/src/app/services/document-list-view.service.ts +++ b/src-ui/src/app/services/document-list-view.service.ts @@ -1,4 +1,4 @@ -import { Injectable, inject } from '@angular/core' +import { Injectable, inject, signal } from '@angular/core' import { ParamMap, Router, UrlTree } from '@angular/router' import { Observable, Subject, takeUntil } from 'rxjs' import { @@ -118,6 +118,7 @@ export class DocumentListViewService { isReloading: boolean = false initialized: boolean = false error: string = null + private readonly stateVersion = signal(0) rangeSelectionAnchorIndex: number lastRangeSelectionToIndex: number @@ -132,6 +133,14 @@ export class DocumentListViewService { private displayFieldsInitialized: boolean = false + private markChanged(): void { + this.stateVersion.update((version) => version + 1) + } + + private trackState(): void { + this.stateVersion() + } + private restoreListViewState(savedState: unknown): ListViewState { const newState = this.defaultListViewState() @@ -159,10 +168,12 @@ export class DocumentListViewService { } get activeSavedViewId() { + this.trackState() return this._activeSavedViewId } get activeSavedViewTitle() { + this.trackState() return this.activeListViewState.title } @@ -184,13 +195,12 @@ export class DocumentListViewService { this.displayFieldsInitialized = true if (this.activeListViewState.displayFields) { this.activeListViewState.displayFields = - this.activeListViewState.displayFields.filter( - (field) => - this.settings.allDisplayFields.find((f) => f.id === field) !== - undefined + this.activeListViewState.displayFields.filter((field) => + this.settings.allDisplayFields().some((f) => f.id === field) ) this.saveDocumentListView() } + this.markChanged() }) } @@ -243,6 +253,7 @@ export class DocumentListViewService { } else { this._activeSavedViewId = null } + this.markChanged() } activateSavedViewWithQueryParams(view: SavedView, queryParams: ParamMap) { @@ -267,6 +278,7 @@ export class DocumentListViewService { this.activeListViewState.displayFields = view.display_fields this.reduceSelectionToFilter() + this.markChanged() if (!this.router.routerState.snapshot.url.includes('/view/')) { this.router.navigate(['view', view.id]) @@ -297,6 +309,7 @@ export class DocumentListViewService { this.activeListViewState.sortField = newState.sortField this.activeListViewState.sortReverse = newState.sortReverse this.activeListViewState.currentPage = newState.currentPage + this.markChanged() this.reload(null, paramsEmpty) // update the params if there aren't any } } @@ -305,6 +318,7 @@ export class DocumentListViewService { this.cancelPending() this.isReloading = true this.error = null + this.markChanged() let activeListViewState = this.activeListViewState this.documentService .listFiltered( @@ -325,6 +339,7 @@ export class DocumentListViewService { activeListViewState.documents = result.results this.selectionData = resultWithSelectionData.selection_data ?? null this.syncSelectedToCurrentPage() + this.markChanged() if (updateQueryParams && !this._activeSavedViewId) { let base = ['/documents'] @@ -343,18 +358,20 @@ export class DocumentListViewService { onFinish() } this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null + this.markChanged() }, error: (error) => { this.isReloading = false if (activeListViewState.currentPage != 1 && error.status == 404) { // this happens when applying a filter: the current page might not be available anymore due to the reduced result set. activeListViewState.currentPage = 1 + this.markChanged() this.reload() } else if ( activeListViewState.sortField.indexOf('custom_field') === 0 && - this.settings.allDisplayFields.find( - (f) => f.id === activeListViewState.sortField - ) === undefined + !this.settings + .allDisplayFields() + .some((f) => f.id === activeListViewState.sortField) ) { // e.g. field was deleted this.sortField = 'created' @@ -381,6 +398,7 @@ export class DocumentListViewService { errorMessage = error.error } this.error = errorMessage + this.markChanged() } }, }) @@ -397,12 +415,14 @@ export class DocumentListViewService { if (resetPage) { this.activeListViewState.currentPage = 1 } + this.markChanged() this.reload() this.reduceSelectionToFilter() this.saveDocumentListView() } get filterRules(): FilterRule[] { + this.trackState() return this.activeListViewState.filterRules } @@ -416,48 +436,58 @@ export class DocumentListViewService { set sortField(field: string) { this.activeListViewState.sortField = field + this.markChanged() this.reload() this.saveDocumentListView() } get sortField(): string { + this.trackState() return this.activeListViewState.sortField } set sortReverse(reverse: boolean) { this.activeListViewState.sortReverse = reverse + this.markChanged() this.reload() this.saveDocumentListView() } get sortReverse(): boolean { + this.trackState() return this.activeListViewState.sortReverse } get collectionSize(): number { + this.trackState() return this.activeListViewState.collectionSize } get currentPage(): number { + this.trackState() return this.activeListViewState.currentPage } set currentPage(page: number) { if (this.activeListViewState.currentPage == page) return this.activeListViewState.currentPage = page + this.markChanged() this.reload() this.saveDocumentListView() } get documents(): Document[] { + this.trackState() return this.activeListViewState.documents } get selected(): Set { + this.trackState() return this.activeListViewState.selected } get allSelected(): boolean { + this.trackState() return this.activeListViewState.allSelected ?? false } @@ -474,16 +504,19 @@ export class DocumentListViewService { setSort(field: string, reverse: boolean) { this.activeListViewState.sortField = field this.activeListViewState.sortReverse = reverse + this.markChanged() this.reload() this.saveDocumentListView() } set displayMode(mode: DisplayMode) { this.activeListViewState.displayMode = mode + this.markChanged() this.saveDocumentListView() } get displayMode(): DisplayMode { + this.trackState() const mode = this.activeListViewState.displayMode ?? DisplayMode.SMALL_CARDS if (mode === ('details' as any)) { // legacy @@ -494,11 +527,13 @@ export class DocumentListViewService { set pageSize(size: number) { this.activeListViewState.pageSize = size + this.markChanged() this.reload() this.saveDocumentListView() } get pageSize(): number { + this.trackState() return ( this.activeListViewState.pageSize ?? this.settings.get(SETTINGS_KEYS.DOCUMENT_LIST_SIZE) @@ -506,17 +541,17 @@ export class DocumentListViewService { } get displayFields(): DisplayField[] { + this.trackState() return this.activeListViewState.displayFields ?? LIST_DEFAULT_DISPLAY_FIELDS } set displayFields(fields: DisplayField[]) { this.activeListViewState.displayFields = this.displayFieldsInitialized - ? fields?.filter( - (field) => - this.settings.allDisplayFields.find((f) => f.id === field) !== - undefined + ? fields?.filter((field) => + this.settings.allDisplayFields().some((f) => f.id === field) ) : fields + this.markChanged() this.saveDocumentListView() } @@ -540,6 +575,7 @@ export class DocumentListViewService { quickFilter(filterRules: FilterRule[]) { this._activeSavedViewId = null + this.markChanged() this.setFilterRules(filterRules) this.router.navigate(['documents']) } @@ -628,6 +664,7 @@ export class DocumentListViewService { this.activeListViewState.allSelected = false this.selected.clear() this.rangeSelectionAnchorIndex = this.lastRangeSelectionToIndex = null + this.markChanged() } reduceSelectionToFilter() { @@ -644,6 +681,7 @@ export class DocumentListViewService { this.selected.delete(id) } } + this.markChanged() }) } } @@ -651,6 +689,7 @@ export class DocumentListViewService { selectAll() { this.activeListViewState.allSelected = true this.syncSelectedToCurrentPage() + this.markChanged() } selectPage() { @@ -659,6 +698,7 @@ export class DocumentListViewService { this.documents.forEach((doc) => { this.selected.add(doc.id) }) + this.markChanged() } isSelected(d: Document) { @@ -673,6 +713,7 @@ export class DocumentListViewService { else this.selected.add(d.id) this.rangeSelectionAnchorIndex = this.documentIndexInCurrentView(d.id) this.lastRangeSelectionToIndex = null + this.markChanged() } selectRangeTo(d: Document) { @@ -710,6 +751,7 @@ export class DocumentListViewService { this.selected.add(d.id) }) this.lastRangeSelectionToIndex = documentToIndex + this.markChanged() } else { // e.g. shift key but was first click this.toggleSelected(d) diff --git a/src-ui/src/app/services/hot-key.service.ts b/src-ui/src/app/services/hot-key.service.ts index 0623e8f87..eeab4644c 100644 --- a/src-ui/src/app/services/hot-key.service.ts +++ b/src-ui/src/app/services/hot-key.service.ts @@ -94,6 +94,6 @@ export class HotKeyService { private openHelpModal() { const modal = this.modalService.open(HotkeyDialogComponent) - modal.componentInstance.hotkeys = this.hotkeys + modal.componentInstance.hotkeys.set(this.hotkeys) } } diff --git a/src-ui/src/app/services/open-documents.service.ts b/src-ui/src/app/services/open-documents.service.ts index ebe90d8e8..f66a3ddca 100644 --- a/src-ui/src/app/services/open-documents.service.ts +++ b/src-ui/src/app/services/open-documents.service.ts @@ -1,4 +1,4 @@ -import { Injectable, inject } from '@angular/core' +import { Injectable, inject, signal } from '@angular/core' import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { Observable, Subject, of } from 'rxjs' import { first } from 'rxjs/operators' @@ -15,6 +15,7 @@ export class OpenDocumentsService { private modalService = inject(NgbModal) private MAX_OPEN_DOCUMENTS = 5 + private readonly stateVersion = signal(0) constructor() { this.load() @@ -36,6 +37,14 @@ export class OpenDocumentsService { private openDocuments: Document[] = [] private dirtyDocuments: Set = new Set() + private trackState(): void { + this.stateVersion() + } + + private markChanged(): void { + this.stateVersion.update((version) => version + 1) + } + refreshDocument(id: number) { let index = this.openDocuments.findIndex((doc) => doc.id == id) if (index > -1) { @@ -53,10 +62,12 @@ export class OpenDocumentsService { } getOpenDocuments(): Document[] { + this.trackState() return this.openDocuments } getOpenDocument(id: number): Document { + this.trackState() return this.openDocuments.find((d) => d.id == id) } @@ -101,10 +112,12 @@ export class OpenDocumentsService { } hasDirty(): boolean { + this.trackState() return this.dirtyDocuments.size > 0 } isDirty(doc: Document): boolean { + this.trackState() return this.dirtyDocuments.has(doc.id) } @@ -170,6 +183,7 @@ export class OpenDocumentsService { } save() { + this.markChanged() try { sessionStorage.setItem( OPEN_DOCUMENT_SERVICE.DOCUMENTS, diff --git a/src-ui/src/app/services/permissions.service.ts b/src-ui/src/app/services/permissions.service.ts index 9de03a761..8a5917e88 100644 --- a/src-ui/src/app/services/permissions.service.ts +++ b/src-ui/src/app/services/permissions.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core' +import { Injectable, signal } from '@angular/core' import { ObjectWithPermissions } from '../data/object-with-permissions' import { User } from '../data/user' @@ -40,16 +40,19 @@ export enum PermissionType { export class PermissionsService { private permissions: string[] private currentUser: User + private readonly permissionsVersion = signal(0) public initialize(permissions: string[], currentUser: User) { this.permissions = permissions this.currentUser = currentUser + this.permissionsVersion.update((version) => version + 1) } public currentUserCan( action: PermissionAction, type: PermissionType ): boolean { + this.permissionsVersion() return ( this.currentUser?.is_superuser || this.permissions?.includes(this.getPermissionCode(action, type)) diff --git a/src-ui/src/app/services/rest/saved-view.service.spec.ts b/src-ui/src/app/services/rest/saved-view.service.spec.ts index c72fb5409..faf4e9328 100644 --- a/src-ui/src/app/services/rest/saved-view.service.spec.ts +++ b/src-ui/src/app/services/rest/saved-view.service.spec.ts @@ -96,7 +96,7 @@ describe(`Additional service tests for SavedViewService`, () => { }) it('should sort dashboard views', () => { - service['savedViews'] = saved_views + service['savedViews'].set(saved_views) jest.spyOn(settingsService, 'get').mockImplementation((key) => { if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_VISIBLE_IDS) return [1, 2, 3] if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [3, 1, 2] @@ -110,7 +110,7 @@ describe(`Additional service tests for SavedViewService`, () => { }) it('should use user-specific dashboard visibility when configured', () => { - service['savedViews'] = saved_views + service['savedViews'].set(saved_views) jest.spyOn(settingsService, 'get').mockImplementation((key) => { if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_VISIBLE_IDS) return [4, 2] if (key === SETTINGS_KEYS.DASHBOARD_VIEWS_SORT_ORDER) return [] @@ -119,7 +119,7 @@ describe(`Additional service tests for SavedViewService`, () => { }) it('should sort sidebar views', () => { - service['savedViews'] = saved_views + service['savedViews'].set(saved_views) jest.spyOn(settingsService, 'get').mockImplementation((key) => { if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_VISIBLE_IDS) return [1, 2, 3] if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return [3, 1, 2] @@ -133,7 +133,7 @@ describe(`Additional service tests for SavedViewService`, () => { }) it('should use user-specific sidebar visibility when configured', () => { - service['savedViews'] = saved_views + service['savedViews'].set(saved_views) jest.spyOn(settingsService, 'get').mockImplementation((key) => { if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_VISIBLE_IDS) return [4, 2] if (key === SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER) return [] diff --git a/src-ui/src/app/services/rest/saved-view.service.ts b/src-ui/src/app/services/rest/saved-view.service.ts index d9baec22b..556f11aad 100644 --- a/src-ui/src/app/services/rest/saved-view.service.ts +++ b/src-ui/src/app/services/rest/saved-view.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http' -import { inject, Injectable } from '@angular/core' +import { inject, Injectable, signal } from '@angular/core' import { combineLatest, Observable, Subject } from 'rxjs' import { takeUntil, tap } from 'rxjs/operators' import { Results } from 'src/app/data/results' @@ -17,8 +17,10 @@ export class SavedViewService extends AbstractPaperlessService { private settingsService = inject(SettingsService) private documentService = inject(DocumentService) - private savedViews: SavedView[] = [] - private savedViewDocumentCounts: Map = new Map() + private readonly savedViews = signal([]) + private readonly savedViewDocumentCounts = signal>( + new Map() + ) private unsubscribeNotifier: Subject = new Subject() constructor() { @@ -37,15 +39,16 @@ export class SavedViewService extends AbstractPaperlessService { tap({ next: (r) => { const views = r.results.map((view) => this.withUserVisibility(view)) - this.savedViews = views + this.savedViews.set(views) r.results = views this._loading = false - this.settingsService.dashboardIsEmpty = + this.settingsService.dashboardIsEmpty.set( this.dashboardViews.length === 0 + ) }, error: () => { this._loading = false - this.settingsService.dashboardIsEmpty = true + this.settingsService.dashboardIsEmpty.set(true) }, }) ) @@ -63,8 +66,8 @@ export class SavedViewService extends AbstractPaperlessService { .subscribe() } - get allViews() { - return this.savedViews + get allViews(): SavedView[] { + return this.savedViews() } private getVisibleViewIds(setting: string): number[] { @@ -95,7 +98,9 @@ export class SavedViewService extends AbstractPaperlessService { } get sidebarViews(): SavedView[] { - const sidebarViews = this.savedViews.filter((v) => this.isSidebarVisible(v)) + const sidebarViews = this.savedViews().filter((v) => + this.isSidebarVisible(v) + ) const sorted: number[] = this.settingsService.get( SETTINGS_KEYS.SIDEBAR_VIEWS_SORT_ORDER @@ -110,7 +115,7 @@ export class SavedViewService extends AbstractPaperlessService { } get dashboardViews(): SavedView[] { - const dashboardViews = this.savedViews.filter((v) => + const dashboardViews = this.savedViews().filter((v) => this.isDashboardVisible(v) ) @@ -176,10 +181,12 @@ export class SavedViewService extends AbstractPaperlessService { } public setDocumentCount(view: SavedView, count: number) { - this.savedViewDocumentCounts.set(view.id, count) + const counts = new Map(this.savedViewDocumentCounts()) + counts.set(view.id, count) + this.savedViewDocumentCounts.set(counts) } public getDocumentCount(view: SavedView): number { - return this.savedViewDocumentCounts.get(view.id) + return this.savedViewDocumentCounts().get(view.id) } } diff --git a/src-ui/src/app/services/settings.service.spec.ts b/src-ui/src/app/services/settings.service.spec.ts index 5190bc549..c91296537 100644 --- a/src-ui/src/app/services/settings.service.spec.ts +++ b/src-ui/src/app/services/settings.service.spec.ts @@ -3,7 +3,7 @@ import { HttpTestingController, provideHttpClientTesting, } from '@angular/common/http/testing' -import { fakeAsync, TestBed, tick } from '@angular/core/testing' +import { TestBed } from '@angular/core/testing' import { FormsModule, ReactiveFormsModule } from '@angular/forms' import { RouterTestingModule } from '@angular/router/testing' import { NgbModule } from '@ng-bootstrap/ng-bootstrap' @@ -122,7 +122,8 @@ describe('SettingsService', () => { expect(req.request.method).toEqual('GET') }) - it('should catch error and show toast on retrieve ui_settings error', fakeAsync(() => { + it('should catch error and show toast on retrieve ui_settings error', () => { + jest.useFakeTimers() const toastSpy = jest.spyOn(toastService, 'showError') httpTestingController .expectOne(`${environment.apiBaseUrl}ui_settings/`) @@ -130,9 +131,10 @@ describe('SettingsService', () => { { detail: 'You do not have permission to perform this action.' }, { status: 403, statusText: 'Forbidden' } ) - tick(500) + jest.advanceTimersByTime(500) expect(toastSpy).toHaveBeenCalled() - })) + jest.useRealTimers() + }) it('calls ui_settings api endpoint with POST on store', () => { let req = httpTestingController.expectOne( @@ -392,22 +394,22 @@ describe('SettingsService', () => { req.flush(ui_settings) settingsService.initializeDisplayFields() expect( - settingsService.allDisplayFields.includes(DEFAULT_DISPLAY_FIELDS[0]) + settingsService.allDisplayFields().includes(DEFAULT_DISPLAY_FIELDS[0]) ).toBeTruthy() // title expect( - settingsService.allDisplayFields.includes(DEFAULT_DISPLAY_FIELDS[4]) + settingsService.allDisplayFields().includes(DEFAULT_DISPLAY_FIELDS[4]) ).toBeFalsy() // correspondent settingsService.set(SETTINGS_KEYS.NOTES_ENABLED, false) settingsService.initializeDisplayFields() expect( - settingsService.allDisplayFields.includes(DEFAULT_DISPLAY_FIELDS[8]) + settingsService.allDisplayFields().includes(DEFAULT_DISPLAY_FIELDS[8]) ).toBeFalsy() // notes jest.spyOn(permissionService, 'currentUserCan').mockReturnValue(true) settingsService.initializeDisplayFields() expect( - settingsService.allDisplayFields.includes(DEFAULT_DISPLAY_FIELDS[4]) + settingsService.allDisplayFields().includes(DEFAULT_DISPLAY_FIELDS[4]) ).toBeTruthy() // correspondent }) @@ -422,12 +424,14 @@ describe('SettingsService', () => { ) settingsService.initializeDisplayFields() expect( - settingsService.allDisplayFields.includes(DEFAULT_DISPLAY_FIELDS[0]) + settingsService.allDisplayFields().includes(DEFAULT_DISPLAY_FIELDS[0]) ).toBeTruthy() expect( - settingsService.allDisplayFields.find( - (f) => f.id === `${DisplayField.CUSTOM_FIELD}${customFields[0].id}` - ).name + settingsService + .allDisplayFields() + .find( + (f) => f.id === `${DisplayField.CUSTOM_FIELD}${customFields[0].id}` + ).name ).toEqual(customFields[0].name) }) }) diff --git a/src-ui/src/app/services/settings.service.ts b/src-ui/src/app/services/settings.service.ts index 8768a2058..2627de5f2 100644 --- a/src-ui/src/app/services/settings.service.ts +++ b/src-ui/src/app/services/settings.service.ts @@ -7,6 +7,7 @@ import { LOCALE_ID, Renderer2, RendererFactory2, + signal, } from '@angular/core' import { Meta } from '@angular/platform-browser' import { CookieService } from 'ngx-cookie-service' @@ -294,7 +295,8 @@ export class SettingsService { protected baseUrl: string = environment.apiBaseUrl + 'ui_settings/' private settings: Record = {} - currentUser: User + private readonly settingsVersion = signal(0) + readonly currentUser = signal(undefined) public settingsSaved: EventEmitter = new EventEmitter() @@ -303,17 +305,14 @@ export class SettingsService { return this._renderer } - public dashboardIsEmpty: boolean = false + readonly dashboardIsEmpty = signal(false) + readonly globalDropzoneEnabled = signal(true) + readonly globalDropzoneActive = signal(false) + readonly organizingSidebarSavedViews = signal(false) - public globalDropzoneEnabled: boolean = true - public globalDropzoneActive: boolean = false - public organizingSidebarSavedViews: boolean = false - - private _allDisplayFields: Array<{ id: DisplayField; name: string }> = + readonly allDisplayFields = signal>( DEFAULT_DISPLAY_FIELDS - public get allDisplayFields(): Array<{ id: DisplayField; name: string }> { - return this._allDisplayFields - } + ) public displayFieldsInit: EventEmitter = new EventEmitter() constructor() { @@ -326,6 +325,10 @@ export class SettingsService { return !UNSAFE_OBJECT_KEYS.has(key) } + public trackChanges(): void { + this.settingsVersion() + } + private assignSafeSettings(source: Record) { if (!source || typeof source !== 'object' || Array.isArray(source)) { return @@ -363,10 +366,10 @@ export class SettingsService { // to update lang cookie if (this.settings['language']?.length) this.setLanguage(this.settings['language']) - this.currentUser = uisettings.user + this.currentUser.set(uisettings.user) this.permissionsService.initialize( uisettings.permissions, - this.currentUser + this.currentUser() ) this.initializeDisplayFields() @@ -375,44 +378,39 @@ export class SettingsService { } public initializeDisplayFields() { - this._allDisplayFields = DEFAULT_DISPLAY_FIELDS + const displayFields = DEFAULT_DISPLAY_FIELDS?.map((field) => { + if ( + field.id === DisplayField.NOTES && + !this.get(SETTINGS_KEYS.NOTES_ENABLED) + ) { + return null + } - this._allDisplayFields = this._allDisplayFields - ?.map((field) => { - if ( - field.id === DisplayField.NOTES && - !this.get(SETTINGS_KEYS.NOTES_ENABLED) - ) { - return null - } + if ( + [ + DisplayField.TITLE, + DisplayField.CREATED, + DisplayField.ADDED, + DisplayField.ASN, + DisplayField.PAGE_COUNT, + DisplayField.SHARED, + ].includes(field.id) + ) { + return field + } - if ( - [ - DisplayField.TITLE, - DisplayField.CREATED, - DisplayField.ADDED, - DisplayField.ASN, - DisplayField.PAGE_COUNT, - DisplayField.SHARED, - ].includes(field.id) - ) { - return field - } + let type: PermissionType = Object.values(PermissionType).find((t) => + t.includes(field.id) + ) + if (field.id === DisplayField.OWNER) { + type = PermissionType.User + } + return this.permissionsService.currentUserCan(PermissionAction.View, type) + ? field + : null + }).filter(Boolean) - let type: PermissionType = Object.values(PermissionType).find((t) => - t.includes(field.id) - ) - if (field.id === DisplayField.OWNER) { - type = PermissionType.User - } - return this.permissionsService.currentUserCan( - PermissionAction.View, - type - ) - ? field - : null - }) - .filter((f) => f) + this.allDisplayFields.set(displayFields) if ( this.permissionsService.currentUserCan( @@ -421,13 +419,15 @@ export class SettingsService { ) ) { this.customFieldsService.listAll().subscribe((r) => { - this._allDisplayFields = this._allDisplayFields.concat( - r.results.map((field) => { - return { - id: `${DisplayField.CUSTOM_FIELD}${field.id}` as any, - name: field.name, - } - }) + this.allDisplayFields.set( + displayFields.concat( + r.results.map((field) => { + return { + id: `${DisplayField.CUSTOM_FIELD}${field.id}` as any, + name: field.name, + } + }) + ) ) this.displayFieldsInit.emit(true) }) @@ -438,8 +438,8 @@ export class SettingsService { get displayName(): string { return ( - this.currentUser.first_name ?? - this.currentUser.username ?? + this.currentUser()?.first_name ?? + this.currentUser()?.username ?? '' ).trim() } @@ -572,7 +572,7 @@ export class SettingsService { // special case to fallback if (key === SETTINGS_KEYS.DEFAULT_PERMS_OWNER && value === undefined) { - return this.currentUser.id + return this.currentUser()?.id } if (value !== undefined) { @@ -606,6 +606,7 @@ export class SettingsService { if (index == keys.length - 1) settingObj[keyPart] = value else settingObj = settingObj[keyPart] }) + this.settingsVersion.update((version) => version + 1) } private settingIsSet(key: string): boolean { @@ -688,7 +689,7 @@ export class SettingsService { } offerTour(): boolean { - return this.dashboardIsEmpty && !this.get(SETTINGS_KEYS.TOUR_COMPLETE) + return this.dashboardIsEmpty() && !this.get(SETTINGS_KEYS.TOUR_COMPLETE) } completeTour() { diff --git a/src-ui/src/app/services/tasks.service.ts b/src-ui/src/app/services/tasks.service.ts index 404db589a..8b71c6923 100644 --- a/src-ui/src/app/services/tasks.service.ts +++ b/src-ui/src/app/services/tasks.service.ts @@ -1,5 +1,5 @@ import { HttpClient } from '@angular/common/http' -import { Injectable, inject } from '@angular/core' +import { Injectable, inject, signal } from '@angular/core' import { Observable, Subject } from 'rxjs' import { first, map, takeUntil, tap } from 'rxjs/operators' import { @@ -23,44 +23,44 @@ export class TasksService { public loading: boolean = false - private fileTasks: PaperlessTask[] = [] + private readonly fileTasks = signal([]) private unsubscribeNotifer: Subject = new Subject() public get total(): number { - return this.fileTasks.length + return this.fileTasks().length } public get allFileTasks(): PaperlessTask[] { - return this.fileTasks.slice(0) + return this.fileTasks().slice(0) } public get queuedFileTasks(): PaperlessTask[] { - return this.fileTasks.filter( + return this.fileTasks().filter( (t) => t.status === PaperlessTaskStatus.Pending ) } public get startedFileTasks(): PaperlessTask[] { - return this.fileTasks.filter( + return this.fileTasks().filter( (t) => t.status === PaperlessTaskStatus.Started ) } public get completedFileTasks(): PaperlessTask[] { - return this.fileTasks.filter( + return this.fileTasks().filter( (t) => t.status === PaperlessTaskStatus.Success ) } public get failedFileTasks(): PaperlessTask[] { - return this.fileTasks.filter( + return this.fileTasks().filter( (t) => t.status === PaperlessTaskStatus.Failure ) } public get needsAttentionTasks(): PaperlessTask[] { - return this.fileTasks.filter((t) => + return this.fileTasks().filter((t) => [PaperlessTaskStatus.Failure, PaperlessTaskStatus.Revoked].includes( t.status ) @@ -81,7 +81,7 @@ export class TasksService { .pipe(map((r) => r.results)) .pipe(takeUntil(this.unsubscribeNotifer), first()) .subscribe((r) => { - this.fileTasks = r + this.fileTasks.set(r) this.loading = false }) } diff --git a/src-ui/src/app/services/upload-documents.service.ts b/src-ui/src/app/services/upload-documents.service.ts index 700e1e91a..7895a377b 100644 --- a/src-ui/src/app/services/upload-documents.service.ts +++ b/src-ui/src/app/services/upload-documents.service.ts @@ -23,6 +23,7 @@ export class UploadDocumentsService { let status = this.websocketStatusService.newFileUpload(file.name) status.message = $localize`Connecting...` + this.websocketStatusService.statusChanged() this.uploadSubscriptions[file.name] = this.documentService .uploadDocument(formData) @@ -35,9 +36,11 @@ export class UploadDocumentsService { event.total ) status.message = $localize`Uploading...` + this.websocketStatusService.statusChanged() } else if (event.type == HttpEventType.Response) { status.taskId = event.body['task_id'] ?? event.body.toString() status.message = $localize`Upload complete, waiting...` + this.websocketStatusService.statusChanged() this.uploadSubscriptions[file.name]?.complete() } }, diff --git a/src-ui/src/app/services/websocket-status.service.spec.ts b/src-ui/src/app/services/websocket-status.service.spec.ts index d6fdbb07d..50ea62c53 100644 --- a/src-ui/src/app/services/websocket-status.service.spec.ts +++ b/src-ui/src/app/services/websocket-status.service.spec.ts @@ -45,11 +45,11 @@ describe('ConsumerStatusService', () => { httpTestingController = TestBed.inject(HttpTestingController) settingsService = TestBed.inject(SettingsService) - settingsService.currentUser = { + settingsService.currentUser.set({ id: 1, username: 'testuser', is_superuser: false, - } + }) websocketStatusService = TestBed.inject(WebsocketStatusService) documentService = TestBed.inject(DocumentService) }) @@ -356,12 +356,12 @@ describe('ConsumerStatusService', () => { }) it('should notify user if user can view or is in group', () => { - settingsService.currentUser = { + settingsService.currentUser.set({ id: 1, username: 'testuser', is_superuser: false, groups: [1], - } + }) websocketStatusService.connect() server.send({ type: WebsocketStatusType.STATUS_UPDATE, diff --git a/src-ui/src/app/services/websocket-status.service.ts b/src-ui/src/app/services/websocket-status.service.ts index 9e09522b7..51707c90e 100644 --- a/src-ui/src/app/services/websocket-status.service.ts +++ b/src-ui/src/app/services/websocket-status.service.ts @@ -1,4 +1,4 @@ -import { Injectable, inject } from '@angular/core' +import { Injectable, inject, signal } from '@angular/core' import { Subject } from 'rxjs' import { environment } from 'src/environments/environment' import { User } from '../data/user' @@ -106,7 +106,7 @@ export class WebsocketStatusService { private statusWebSocket: WebSocket - private consumerStatus: FileStatus[] = [] + private readonly consumerStatus = signal([]) private readonly documentDetectedSubject = new Subject() private readonly documentConsumptionFinishedSubject = @@ -119,14 +119,14 @@ export class WebsocketStatusService { private get(taskId: string, filename?: string) { let status = - this.consumerStatus.find((e) => e.taskId == taskId) || - this.consumerStatus.find( + this.consumerStatus().find((e) => e.taskId == taskId) || + this.consumerStatus().find( (e) => e.filename == filename && e.taskId == null ) let created = false if (!status) { status = new FileStatus() - this.consumerStatus.push(status) + this.consumerStatus.update((statuses) => [...statuses, status]) created = true } status.taskId = taskId @@ -137,24 +137,30 @@ export class WebsocketStatusService { newFileUpload(filename: string): FileStatus { let status = new FileStatus() status.filename = filename - this.consumerStatus.push(status) + this.consumerStatus.update((statuses) => [...statuses, status]) return status } + statusChanged() { + this.consumerStatus.update((statuses) => [...statuses]) + } + getConsumerStatus(phase?: FileStatusPhase) { if (phase != null) { - return this.consumerStatus.filter((s) => s.phase == phase) + return this.consumerStatus().filter((s) => s.phase == phase) } else { - return this.consumerStatus + return this.consumerStatus() } } getConsumerStatusNotCompleted() { - return this.consumerStatus.filter((s) => s.phase < FileStatusPhase.SUCCESS) + return this.consumerStatus().filter( + (s) => s.phase < FileStatusPhase.SUCCESS + ) } getConsumerStatusCompleted() { - return this.consumerStatus.filter( + return this.consumerStatus().filter( (s) => s.phase == FileStatusPhase.FAILED || s.phase == FileStatusPhase.SUCCESS ) @@ -211,7 +217,7 @@ export class WebsocketStatusService { groups_can_view?: number[] }): boolean { // see paperless.consumers.StatusConsumer._can_view - const user: User = this.settingsService.currentUser + const user: User = this.settingsService.currentUser() return ( !messageData.owner_id || user.is_superuser || @@ -250,6 +256,7 @@ export class WebsocketStatusService { if (messageData.status in FileStatusPhase) { status.phase = FileStatusPhase[messageData.status] } + this.statusChanged() switch (status.phase) { case FileStatusPhase.STARTED: @@ -281,6 +288,7 @@ export class WebsocketStatusService { fail(status: FileStatus, message: string) { status.message = message status.phase = FileStatusPhase.FAILED + this.statusChanged() this.documentConsumptionFailedSubject.next(status) } @@ -294,24 +302,28 @@ export class WebsocketStatusService { dismiss(status: FileStatus) { let index if (status.taskId != null) { - index = this.consumerStatus.findIndex((s) => s.taskId == status.taskId) + index = this.consumerStatus().findIndex((s) => s.taskId == status.taskId) } else { - index = this.consumerStatus.findIndex( + index = this.consumerStatus().findIndex( (s) => s.filename == status.filename ) } if (index > -1) { - this.consumerStatus.splice(index, 1) + this.consumerStatus.update((statuses) => + statuses.filter((_, statusIndex) => statusIndex !== index) + ) } } dismissCompleted() { - this.consumerStatus = this.consumerStatus.filter( - (status) => - ![FileStatusPhase.SUCCESS, FileStatusPhase.FAILED].includes( - status.phase - ) + this.consumerStatus.update((statuses) => + statuses.filter( + (status) => + ![FileStatusPhase.SUCCESS, FileStatusPhase.FAILED].includes( + status.phase + ) + ) ) } diff --git a/src-ui/src/environments/environment.ts b/src-ui/src/environments/environment.ts index e5823e27e..83567bc40 100644 --- a/src-ui/src/environments/environment.ts +++ b/src-ui/src/environments/environment.ts @@ -13,12 +13,3 @@ export const environment = { webSocketProtocol: 'ws:', webSocketBaseUrl: '/ws/', } - -/* - * For easier debugging in development mode, you can import the following file - * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. - * - * This import should be commented out in production mode because it will have a negative impact - * on performance if an error is thrown. - */ -// import 'zone.js/plugins/zone-error'; // Included with Angular CLI. diff --git a/src-ui/src/main.ts b/src-ui/src/main.ts index 0771b75c6..88c3431c5 100644 --- a/src-ui/src/main.ts +++ b/src-ui/src/main.ts @@ -2,7 +2,7 @@ import { importProvidersFrom, inject, provideAppInitializer, - provideZoneChangeDetection, + provideZonelessChangeDetection, } from '@angular/core' import { DragDropModule } from '@angular/cdk/drag-drop' @@ -378,7 +378,7 @@ const icons = { bootstrapApplication(AppComponent, { providers: [ - provideZoneChangeDetection(), + provideZonelessChangeDetection(), importProvidersFrom( BrowserModule, AppRoutingModule, diff --git a/src-ui/src/polyfills.ts b/src-ui/src/polyfills.ts index ac9c93e4a..b2125ee9d 100644 --- a/src-ui/src/polyfills.ts +++ b/src-ui/src/polyfills.ts @@ -6,11 +6,6 @@ import '@angular/localize/init' * This file includes polyfills needed by Angular and is loaded before the app. * You can add your own extra polyfills to this file. * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * * The current setup is for so-called "evergreen" browsers; the last versions of browsers that * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. @@ -18,39 +13,6 @@ import '@angular/localize/init' * Learn more in https://angular.io/guide/browser-support */ -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js' // Included with Angular CLI. - /*************************************************************************************************** * APPLICATION IMPORTS */ diff --git a/src-ui/tsconfig.json b/src-ui/tsconfig.json index 80af2f277..409ca51b8 100644 --- a/src-ui/tsconfig.json +++ b/src-ui/tsconfig.json @@ -10,6 +10,8 @@ "experimentalDecorators": true, "moduleResolution": "bundler", "importHelpers": true, + "ignoreDeprecations": "6.0", + "strict": false, "target": "ES2022", "module": "es2020", "lib": [ @@ -20,5 +22,6 @@ }, "angularCompilerOptions": { "enableI18nLegacyMessageIdFormat": false, + "strictTemplates": false } } diff --git a/src/documents/sanity_checker.py b/src/documents/sanity_checker.py index b824e6683..413ebff32 100644 --- a/src/documents/sanity_checker.py +++ b/src/documents/sanity_checker.py @@ -45,6 +45,12 @@ class SanityCheckMessages: def __init__(self) -> None: self._messages: dict[int | None, list[MessageEntry]] = defaultdict(list) + self._document_pks: set[int] = set() + self._document_error_pks: set[int] = set() + self._document_warning_pks: set[int] = set() + self._document_info_pks: set[int] = set() + self._document_error_issue_count: int = 0 + self._document_warning_issue_count: int = 0 self.has_error: bool = False self.has_warning: bool = False self.has_info: bool = False @@ -56,20 +62,33 @@ class SanityCheckMessages: # -- Recording ---------------------------------------------------------- + def _add_document_issue(self, doc_pk: int, document_pks: set[int]) -> bool: + if doc_pk not in self._document_pks: + self._document_pks.add(doc_pk) + self.document_count += 1 + + if doc_pk in document_pks: + return False + + document_pks.add(doc_pk) + return True + def error(self, doc_pk: int | None, message: str) -> None: self._messages[doc_pk].append({"level": logging.ERROR, "message": message}) self.has_error = True if doc_pk is not None: - self.document_count += 1 - self.document_error_count += 1 + self._document_error_issue_count += 1 + if self._add_document_issue(doc_pk, self._document_error_pks): + self.document_error_count += 1 def warning(self, doc_pk: int | None, message: str) -> None: self._messages[doc_pk].append({"level": logging.WARNING, "message": message}) self.has_warning = True if doc_pk is not None: - self.document_count += 1 - self.document_warning_count += 1 + self._document_warning_issue_count += 1 + if self._add_document_issue(doc_pk, self._document_warning_pks): + self.document_warning_count += 1 else: # This is the only type of global message we do right now self.global_warning_count += 1 @@ -78,8 +97,10 @@ class SanityCheckMessages: self._messages[doc_pk].append({"level": logging.INFO, "message": message}) self.has_info = True - if doc_pk is not None: - self.document_count += 1 + if doc_pk is not None and self._add_document_issue( + doc_pk, + self._document_info_pks, + ): self.document_info_count += 1 # -- Iteration / query -------------------------------------------------- @@ -105,8 +126,8 @@ class SanityCheckMessages: def total_issue_count(self) -> int: """Total number of error and warning messages across all documents and global.""" return ( - self.document_error_count - + self.document_warning_count + self._document_error_issue_count + + self._document_warning_issue_count + self.global_warning_count ) diff --git a/src/documents/tests/test_sanity_check.py b/src/documents/tests/test_sanity_check.py index 568e3e444..f282f3ea5 100644 --- a/src/documents/tests/test_sanity_check.py +++ b/src/documents/tests/test_sanity_check.py @@ -12,6 +12,7 @@ from typing import TYPE_CHECKING import pytest +from documents.sanity_checker import SanityCheckMessages from documents.sanity_checker import check_sanity if TYPE_CHECKING: @@ -21,6 +22,26 @@ if TYPE_CHECKING: from documents.tests.conftest import PaperlessDirs +class TestSanityCheckMessages: + def test_document_counts_are_unique_per_severity(self) -> None: + messages = SanityCheckMessages() + + messages.error(1, "first error") + messages.error(1, "second error") + messages.warning(1, "first warning") + messages.warning(1, "second warning") + messages.info(1, "first info") + messages.info(1, "second info") + messages.warning(None, "global warning") + + assert messages.document_count == 1 + assert messages.document_error_count == 1 + assert messages.document_warning_count == 1 + assert messages.document_info_count == 1 + assert messages.global_warning_count == 1 + assert messages.total_issue_count == 5 + + @pytest.mark.django_db class TestCheckSanityNoDocuments: """Sanity checks against an empty archive.""" diff --git a/src/documents/tests/test_workflows.py b/src/documents/tests/test_workflows.py index 243f9609e..329ed0af0 100644 --- a/src/documents/tests/test_workflows.py +++ b/src/documents/tests/test_workflows.py @@ -1847,6 +1847,45 @@ class TestWorkflows( self.assertEqual(doc.title, "Doc {created_year]") + def test_document_added_malformed_title_template_falls_back(self) -> None: + """ + GIVEN: + - Existing workflow with added trigger type + - Assign title field is malformed Jinja2 syntax + WHEN: + - File that matches is added + THEN: + - Title assignment is skipped and the original title is kept + """ + trigger = WorkflowTrigger.objects.create( + type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_ADDED, + filter_filename="*sample*", + ) + action = WorkflowAction.objects.create( + assign_title="Doc {{ unclosed", + ) + w = Workflow.objects.create( + name="Workflow 1", + order=0, + ) + w.triggers.add(trigger) + w.actions.add(action) + w.save() + + doc = Document.objects.create( + original_filename="sample.pdf", + title="sample test", + content="Hello world bar", + ) + + document_consumption_finished.send( + sender=self.__class__, + document=doc, + ) + + doc.refresh_from_db() + self.assertEqual(doc.title, "sample test") + def test_document_updated_workflow_ignores_version_documents(self) -> None: trigger = WorkflowTrigger.objects.create( type=WorkflowTrigger.WorkflowTriggerType.DOCUMENT_UPDATED, diff --git a/src/documents/workflows/mutations.py b/src/documents/workflows/mutations.py index 4945d00d5..7dbb317a8 100644 --- a/src/documents/workflows/mutations.py +++ b/src/documents/workflows/mutations.py @@ -40,7 +40,7 @@ def apply_assignment_to_document( if action.assign_title: try: - document.title = parse_w_workflow_placeholders( + title = parse_w_workflow_placeholders( action.assign_title, document.correspondent.name if document.correspondent else "", document.document_type.name if document.document_type else "", @@ -53,6 +53,8 @@ def apply_assignment_to_document( "", # no urls in titles document.pk, ) + if title: + document.title = title except Exception: # pragma: no cover logger.exception( f"Error occurred parsing title assignment '{action.assign_title}', falling back to original", diff --git a/src/locale/en_US/LC_MESSAGES/django.po b/src/locale/en_US/LC_MESSAGES/django.po index 609030003..629693d88 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: 2026-06-03 22:14+0000\n" +"POT-Creation-Date: 2026-06-23 14:33+0000\n" "PO-Revision-Date: 2022-02-17 04:17\n" "Last-Translator: \n" "Language-Team: English\n" @@ -21,39 +21,39 @@ msgstr "" msgid "Documents" msgstr "" -#: documents/filters.py:463 +#: documents/filters.py:464 msgid "Value must be valid JSON." msgstr "" -#: documents/filters.py:482 +#: documents/filters.py:483 msgid "Invalid custom field query expression" msgstr "" -#: documents/filters.py:492 +#: documents/filters.py:493 msgid "Invalid expression list. Must be nonempty." msgstr "" -#: documents/filters.py:513 +#: documents/filters.py:514 msgid "Invalid logical operator {op!r}" msgstr "" -#: documents/filters.py:527 +#: documents/filters.py:528 msgid "Maximum number of query conditions exceeded." msgstr "" -#: documents/filters.py:591 +#: documents/filters.py:592 msgid "{name!r} is not a valid custom field." msgstr "" -#: documents/filters.py:628 +#: documents/filters.py:629 msgid "{data_type} does not support query expr {expr!r}." msgstr "" -#: documents/filters.py:743 documents/models.py:136 +#: documents/filters.py:744 documents/models.py:136 msgid "Maximum nesting depth exceeded." msgstr "" -#: documents/filters.py:990 +#: documents/filters.py:1052 msgid "Custom field not found" msgstr "" @@ -1351,49 +1351,49 @@ msgstr "" msgid "workflow runs" msgstr "" -#: documents/serialisers.py:463 documents/serialisers.py:815 -#: documents/serialisers.py:2681 documents/views.py:295 documents/views.py:2468 +#: documents/serialisers.py:503 documents/serialisers.py:855 +#: documents/serialisers.py:2744 documents/views.py:297 documents/views.py:2482 #: paperless_mail/serialisers.py:155 msgid "Insufficient permissions." msgstr "" -#: documents/serialisers.py:651 +#: documents/serialisers.py:691 msgid "Invalid color." msgstr "" -#: documents/serialisers.py:2175 +#: documents/serialisers.py:2216 #, python-format msgid "File type %(type)s not supported" msgstr "" -#: documents/serialisers.py:2219 +#: documents/serialisers.py:2260 #, python-format msgid "Custom field id must be an integer: %(id)s" msgstr "" -#: documents/serialisers.py:2226 +#: documents/serialisers.py:2267 #, python-format msgid "Custom field with id %(id)s does not exist" msgstr "" -#: documents/serialisers.py:2243 documents/serialisers.py:2253 +#: documents/serialisers.py:2284 documents/serialisers.py:2294 msgid "" "Custom fields must be a list of integers or an object mapping ids to values." msgstr "" -#: documents/serialisers.py:2248 +#: documents/serialisers.py:2289 msgid "Some custom fields don't exist or were specified twice." msgstr "" -#: documents/serialisers.py:2395 +#: documents/serialisers.py:2436 msgid "Invalid variable detected." msgstr "" -#: documents/serialisers.py:2737 +#: documents/serialisers.py:2800 msgid "Duplicate document identifiers are not allowed." msgstr "" -#: documents/serialisers.py:2767 documents/views.py:4345 +#: documents/serialisers.py:2830 documents/views.py:4429 #, python-format msgid "Documents not found: %(ids)s" msgstr "" @@ -1661,32 +1661,36 @@ msgstr "" msgid "Unable to parse URI {value}" msgstr "" -#: documents/views.py:288 documents/views.py:2465 +#: documents/views.py:290 documents/views.py:2479 msgid "Invalid more_like_id" msgstr "" -#: documents/views.py:1511 +#: documents/views.py:1513 msgid "Invalid AI configuration." msgstr "" -#: documents/views.py:2290 documents/views.py:2606 +#: documents/views.py:1522 +msgid "AI backend request timed out." +msgstr "" + +#: documents/views.py:2304 documents/views.py:2625 msgid "Specify only one of text, title_search, query, or more_like_id." msgstr "" -#: documents/views.py:4357 +#: documents/views.py:4441 #, python-format msgid "Insufficient permissions to share document %(id)s." msgstr "" -#: documents/views.py:4403 +#: documents/views.py:4487 msgid "Bundle is already being processed." msgstr "" -#: documents/views.py:4463 +#: documents/views.py:4547 msgid "The share link bundle is still being prepared. Please try again later." msgstr "" -#: documents/views.py:4473 +#: documents/views.py:4557 msgid "The share link bundle is unavailable." msgstr "" @@ -1935,154 +1939,158 @@ msgid "Sets the LLM output language" msgstr "" #: paperless/models.py:370 +msgid "Sets the LLM timeout in seconds" +msgstr "" + +#: paperless/models.py:376 msgid "paperless application settings" msgstr "" -#: paperless/settings/__init__.py:539 +#: paperless/settings/__init__.py:545 msgid "English (US)" msgstr "" -#: paperless/settings/__init__.py:540 +#: paperless/settings/__init__.py:546 msgid "Arabic" msgstr "" -#: paperless/settings/__init__.py:541 +#: paperless/settings/__init__.py:547 msgid "Afrikaans" msgstr "" -#: paperless/settings/__init__.py:542 +#: paperless/settings/__init__.py:548 msgid "Belarusian" msgstr "" -#: paperless/settings/__init__.py:543 +#: paperless/settings/__init__.py:549 msgid "Bulgarian" msgstr "" -#: paperless/settings/__init__.py:544 +#: paperless/settings/__init__.py:550 msgid "Catalan" msgstr "" -#: paperless/settings/__init__.py:545 +#: paperless/settings/__init__.py:551 msgid "Czech" msgstr "" -#: paperless/settings/__init__.py:546 +#: paperless/settings/__init__.py:552 msgid "Danish" msgstr "" -#: paperless/settings/__init__.py:547 +#: paperless/settings/__init__.py:553 msgid "German" msgstr "" -#: paperless/settings/__init__.py:548 +#: paperless/settings/__init__.py:554 msgid "Greek" msgstr "" -#: paperless/settings/__init__.py:549 +#: paperless/settings/__init__.py:555 msgid "English (GB)" msgstr "" -#: paperless/settings/__init__.py:550 +#: paperless/settings/__init__.py:556 msgid "Spanish" msgstr "" -#: paperless/settings/__init__.py:551 +#: paperless/settings/__init__.py:557 msgid "Persian" msgstr "" -#: paperless/settings/__init__.py:552 +#: paperless/settings/__init__.py:558 msgid "Finnish" msgstr "" -#: paperless/settings/__init__.py:553 +#: paperless/settings/__init__.py:559 msgid "French" msgstr "" -#: paperless/settings/__init__.py:554 +#: paperless/settings/__init__.py:560 msgid "Hungarian" msgstr "" -#: paperless/settings/__init__.py:555 +#: paperless/settings/__init__.py:561 msgid "Indonesian" msgstr "" -#: paperless/settings/__init__.py:556 +#: paperless/settings/__init__.py:562 msgid "Italian" msgstr "" -#: paperless/settings/__init__.py:557 +#: paperless/settings/__init__.py:563 msgid "Japanese" msgstr "" -#: paperless/settings/__init__.py:558 +#: paperless/settings/__init__.py:564 msgid "Korean" msgstr "" -#: paperless/settings/__init__.py:559 +#: paperless/settings/__init__.py:565 msgid "Luxembourgish" msgstr "" -#: paperless/settings/__init__.py:560 +#: paperless/settings/__init__.py:566 msgid "Norwegian" msgstr "" -#: paperless/settings/__init__.py:561 +#: paperless/settings/__init__.py:567 msgid "Dutch" msgstr "" -#: paperless/settings/__init__.py:562 +#: paperless/settings/__init__.py:568 msgid "Polish" msgstr "" -#: paperless/settings/__init__.py:563 +#: paperless/settings/__init__.py:569 msgid "Portuguese (Brazil)" msgstr "" -#: paperless/settings/__init__.py:564 +#: paperless/settings/__init__.py:570 msgid "Portuguese" msgstr "" -#: paperless/settings/__init__.py:565 +#: paperless/settings/__init__.py:571 msgid "Romanian" msgstr "" -#: paperless/settings/__init__.py:566 +#: paperless/settings/__init__.py:572 msgid "Russian" msgstr "" -#: paperless/settings/__init__.py:567 +#: paperless/settings/__init__.py:573 msgid "Slovak" msgstr "" -#: paperless/settings/__init__.py:568 +#: paperless/settings/__init__.py:574 msgid "Slovenian" msgstr "" -#: paperless/settings/__init__.py:569 +#: paperless/settings/__init__.py:575 msgid "Serbian" msgstr "" -#: paperless/settings/__init__.py:570 +#: paperless/settings/__init__.py:576 msgid "Swedish" msgstr "" -#: paperless/settings/__init__.py:571 +#: paperless/settings/__init__.py:577 msgid "Turkish" msgstr "" -#: paperless/settings/__init__.py:572 +#: paperless/settings/__init__.py:578 msgid "Ukrainian" msgstr "" -#: paperless/settings/__init__.py:573 +#: paperless/settings/__init__.py:579 msgid "Vietnamese" msgstr "" -#: paperless/settings/__init__.py:574 +#: paperless/settings/__init__.py:580 msgid "Chinese Simplified" msgstr "" -#: paperless/settings/__init__.py:575 +#: paperless/settings/__init__.py:581 msgid "Chinese Traditional" msgstr "" diff --git a/src/paperless_ai/ai_classifier.py b/src/paperless_ai/ai_classifier.py index f9c2f1e06..41bf8e903 100644 --- a/src/paperless_ai/ai_classifier.py +++ b/src/paperless_ai/ai_classifier.py @@ -87,7 +87,7 @@ def build_localization_prompt(suggestions: dict, output_language: str) -> str: Return the same JSON schema with all fields present. Suggestions: - {json.dumps(suggestions)} + {json.dumps(suggestions, ensure_ascii=False)} """.strip() diff --git a/src/paperless_ai/tests/test_ai_classifier.py b/src/paperless_ai/tests/test_ai_classifier.py index 45822b14b..470c7fe07 100644 --- a/src/paperless_ai/tests/test_ai_classifier.py +++ b/src/paperless_ai/tests/test_ai_classifier.py @@ -239,6 +239,23 @@ def test_get_language_name_falls_back_to_language_code(): assert get_language_name("zz-zz") == "zz-zz" +def test_build_localization_prompt_preserves_unicode_characters(): + prompt = build_localization_prompt( + { + "title": "Gebührenbescheid", + "tags": [], + "correspondents": [], + "document_types": [], + "storage_paths": [], + "dates": [], + }, + output_language="de-de", + ) + + assert "Gebührenbescheid" in prompt + assert "\\u00fc" not in prompt + + @patch("paperless_ai.ai_classifier.query_similar_documents") def test_get_context_for_document( mock_query_similar_documents,