diff --git a/.bazelversion b/.bazelversion index e81e85b81044..5942a0d3a0e7 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.6.2 +7.7.1 diff --git a/.github/workflows/assistant-to-the-branch-manager.yml b/.github/workflows/assistant-to-the-branch-manager.yml index 439bf1a78f22..26d7170c03d0 100644 --- a/.github/workflows/assistant-to-the-branch-manager.yml +++ b/.github/workflows/assistant-to-the-branch-manager.yml @@ -13,9 +13,9 @@ jobs: assistant_to_the_branch_manager: runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - - uses: angular/dev-infra/github-actions/branch-manager@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + - uses: angular/dev-infra/github-actions/branch-manager@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ddd9d5d60788..5cae93ad550a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,14 +21,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Generate JSON schema types # Schema types are required to correctly lint the TypeScript code - run: pnpm admin build-schema + run: pnpm run build-schema - name: Run ESLint run: pnpm lint --cache-strategy content - name: Validate NgBot Configuration @@ -44,11 +44,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -61,11 +61,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -85,13 +85,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -101,11 +101,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Install node modules @@ -139,7 +139,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests @@ -162,18 +162,18 @@ jobs: matrix: os: [ubuntu-latest] node: [22] - subset: [yarn, pnpm] + subset: [yarn, pnpm, bun] shard: [0, 1, 2] runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -192,13 +192,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run CLI E2E tests @@ -212,13 +212,13 @@ jobs: SAUCE_TUNNEL_IDENTIFIER: angular-cli-${{ github.workflow }}-${{ github.run_number }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: google_credential: ${{ secrets.RBE_TRUSTED_BUILDS_USER }} - name: Run E2E Browser tests @@ -248,11 +248,11 @@ jobs: CIRCLE_BRANCH: ${{ github.ref_name }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - run: pnpm admin snapshots --verbose env: SNAPSHOT_BUILDS_GITHUB_TOKEN: ${{ secrets.SNAPSHOT_BUILDS_GITHUB_TOKEN }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 64598f02b0da..531005ac0e0f 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -19,16 +19,16 @@ jobs: fail-fast: false steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - name: Initialize CodeQL - uses: github/codeql-action/init@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/init@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: languages: javascript-typescript build-mode: none config-file: .github/codeql/config.yml - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/analyze@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: category: '/language:javascript-typescript' diff --git a/.github/workflows/dev-infra.yml b/.github/workflows/dev-infra.yml index 512efe35b754..b53a65d9956b 100644 --- a/.github/workflows/dev-infra.yml +++ b/.github/workflows/dev-infra.yml @@ -12,14 +12,14 @@ jobs: labels: runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: angular/dev-infra/github-actions/pull-request-labeling@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + - uses: angular/dev-infra/github-actions/pull-request-labeling@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} post_approval_changes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 - - uses: angular/dev-infra/github-actions/post-approval-changes@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + - uses: angular/dev-infra/github-actions/post-approval-changes@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/feature-requests.yml b/.github/workflows/feature-requests.yml index c74b6262bf26..dab00bf96bc2 100644 --- a/.github/workflows/feature-requests.yml +++ b/.github/workflows/feature-requests.yml @@ -16,6 +16,6 @@ jobs: if: github.repository == 'angular/angular-cli' runs-on: ubuntu-latest steps: - - uses: angular/dev-infra/github-actions/feature-request@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + - uses: angular/dev-infra/github-actions/feature-request@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae with: angular-robot-key: ${{ secrets.ANGULAR_ROBOT_PRIVATE_KEY }} diff --git a/.github/workflows/perf.yml b/.github/workflows/perf.yml index 736678ed64de..5e7bb8afa786 100644 --- a/.github/workflows/perf.yml +++ b/.github/workflows/perf.yml @@ -23,7 +23,7 @@ jobs: workflows: ${{ steps.workflows.outputs.workflows }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - id: workflows @@ -38,9 +38,9 @@ jobs: workflow: ${{ fromJSON(needs.list.outputs.workflows) }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile # We utilize the google-github-actions/auth action to allow us to get an active credential using workflow diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 3250a64e0c03..d0c220417222 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -20,7 +20,7 @@ jobs: outputs: snapshots: ${{ steps.filter.outputs.snapshots }} steps: - - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 @@ -34,9 +34,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup ESLint Caching uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: @@ -46,7 +46,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Generate JSON schema types # Schema types are required to correctly lint the TypeScript code - run: pnpm admin build-schema + run: pnpm run build-schema - name: Run ESLint run: pnpm lint --cache-strategy content - name: Validate NgBot Configuration @@ -55,8 +55,6 @@ jobs: run: pnpm ts-circular-deps check - name: Run Validation run: pnpm admin validate - - name: Check Package Licenses - uses: angular/dev-infra/github-actions/linting/licenses@ab6a00e9a219c2169ae0540cc5a32be5f481e004 - name: Check tooling setup run: pnpm check-tooling-setup - name: Check commit message @@ -67,16 +65,18 @@ jobs: # Code formatting checks are only done on pull requests as its too late to validate once # it has been merged. run: pnpm ng-dev format changed --check ${{ github.event.pull_request.base.sha }} + - name: Check Package Licenses + uses: angular/dev-infra/github-actions/linting/licenses@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae build: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Build release targets @@ -93,11 +93,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Run module and package tests @@ -115,13 +115,13 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Run CLI E2E tests run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests/legacy-cli:e2e.${{ matrix.subset }}_node${{ matrix.node }} @@ -129,11 +129,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Build E2E tests for Windows on Linux @@ -157,7 +157,7 @@ jobs: runs-on: windows-2025 steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Download built Windows E2E tests @@ -180,18 +180,18 @@ jobs: matrix: os: [ubuntu-latest] node: [22] - subset: [yarn, pnpm] + subset: [yarn, pnpm, bun] shard: [0, 1, 2] runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Run CLI E2E tests run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=3 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests/legacy-cli:e2e.${{ matrix.subset }}_node${{ matrix.node }} @@ -208,12 +208,12 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: Initialize environment - uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/npm/checkout-and-setup-node@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Install node modules run: pnpm install --frozen-lockfile - name: Setup Bazel - uses: angular/dev-infra/github-actions/bazel/setup@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/setup@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Setup Bazel RBE - uses: angular/dev-infra/github-actions/bazel/configure-remote@ab6a00e9a219c2169ae0540cc5a32be5f481e004 + uses: angular/dev-infra/github-actions/bazel/configure-remote@95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae - name: Run CLI E2E tests run: pnpm bazel test --test_env=E2E_SHARD_TOTAL=6 --test_env=E2E_SHARD_INDEX=${{ matrix.shard }} --config=e2e //tests/legacy-cli:e2e.snapshots.${{ matrix.subset }}_node${{ matrix.node }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0dde6acdc344..8a268a8211e6 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -25,7 +25,7 @@ jobs: steps: - name: 'Checkout code' - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false @@ -46,6 +46,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@16140ae1a102900babc80a33c44059580f687047 # v4.30.9 + uses: github/codeql-action/upload-sarif@fdbfb4d2750291e159f0156def62b853c2798ca2 # v4.31.5 with: sarif_file: results.sarif diff --git a/.ng-dev/caretaker.mjs b/.ng-dev/caretaker.mjs index a16e023b1cd0..d21e783f4af5 100644 --- a/.ng-dev/caretaker.mjs +++ b/.ng-dev/caretaker.mjs @@ -1,6 +1,6 @@ /** * The configuration for `ng-dev caretaker` commands. - * + * * @type { import("@angular/ng-dev").CaretakerConfig } */ export const caretaker = { @@ -14,5 +14,4 @@ export const caretaker = { query: `is:pr is:open label:"action: merge-assistance"`, }, ], - caretakerGroup: 'angular-cli-caretaker', }; diff --git a/.ng-dev/commit-message.mjs b/.ng-dev/commit-message.mjs index 94fe91d8f727..4a3b270ce0a7 100644 --- a/.ng-dev/commit-message.mjs +++ b/.ng-dev/commit-message.mjs @@ -9,7 +9,6 @@ export const commitMessage = { maxLineLength: Infinity, minBodyLength: 0, minBodyLengthTypeExcludes: ['docs'], - disallowFixup: true, // Note: When changing this logic, also change the `contributing.ejs` file. scopes: packages.map(({ name }) => name), }; diff --git a/.ng-dev/pull-request.mjs b/.ng-dev/pull-request.mjs index 8beefa10c5fd..1f2d84d7ccba 100644 --- a/.ng-dev/pull-request.mjs +++ b/.ng-dev/pull-request.mjs @@ -1,12 +1,12 @@ /** * Configuration for the merge tool in `ng-dev`. This sets up the labels which * are respected by the merge script (e.g. the target labels). - * + * * @type { import("@angular/ng-dev").PullRequestConfig } */ export const pullRequest = { githubApiMerge: { - default: 'rebase', + default: 'auto', labels: [{ pattern: 'merge: squash commits', method: 'squash' }], }, }; diff --git a/.nvmrc b/.nvmrc index aa50a62f2194..5767036af0e2 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.21.0 +22.21.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index eb4aff1174eb..ce849578115d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,62 +1,223 @@ - + -# 20.3.7 (2025-10-22) +# 21.0.1 (2025-11-26) -### @angular-devkit/schematics +### @angular/cli -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | -| [a31533cf4](https://github.com/angular/angular-cli/commit/a31533cf492048f62a41b9c09e53779269ee172d) | fix | respect `--force` option when schematic contains `host.create` | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------------------------------------- | +| [363496ae0](https://github.com/angular/angular-cli/commit/363496ae0d2850545274cd7fe4dc6902ccb64e10) | fix | ensure dependencies are resolved correctly for node modules directory check | + +### @schematics/angular + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------- | +| [2f58705cb](https://github.com/angular/angular-cli/commit/2f58705cb5389019ceb49616a0e4ec3f92a558ed) | fix | add missing imports for lifecycle hooks in jasmine-vitest migration | +| [c973bb9ca](https://github.com/angular/angular-cli/commit/c973bb9cafc8d59b901a9d763347f4b615257867) | fix | add mock names to createSpyObj transformation | +| [4534c9848](https://github.com/angular/angular-cli/commit/4534c9848745eea516bdb58d86914252c35b5b9c) | fix | do not set `esModuleInterop` and `moduleResolution` when module is `preserve` | +| [16d898e75](https://github.com/angular/angular-cli/commit/16d898e7587036d68786cebe764da08304559d41) | fix | fix migration of `jasmine.clock().mockDate()` | +| [21c3eac72](https://github.com/angular/angular-cli/commit/21c3eac726c198132af760ffacc0dab9dfccb430) | fix | handle createSpyObj without base name on refactor-jasmine-vitest | +| [b8c99aa4c](https://github.com/angular/angular-cli/commit/b8c99aa4c909647285d1dcc61a2bb97a36100c63) | fix | improve safety of done callback transformation | +| [4a71e06fc](https://github.com/angular/angular-cli/commit/4a71e06fcafaadbcb820d285c0c186aa0e92f158) | fix | silently skip when the build target already uses one of the new builders | +| [2ffdae421](https://github.com/angular/angular-cli/commit/2ffdae42149b0f3da44f96271af1bca6d09b7ed5) | fix | support testRunner option in library schematic | +| [145de4a58](https://github.com/angular/angular-cli/commit/145de4a584ce8f72746704547282299306d9bafb) | fix | warn about loose matching in arrayWithExactContents | ### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------ | -| [8cdda111c](https://github.com/angular/angular-cli/commit/8cdda111cc0b343aa5eb6a7ccbad93302a543226) | fix | resolve Angular locale data namespace in esbuild | -| [5847ccc54](https://github.com/angular/angular-cli/commit/5847ccc545e54eb77a78b2435db7970faf748156) | fix | update `vite` to `7.11.1` | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | +| [d097df2d7](https://github.com/angular/angular-cli/commit/d097df2d7088dd2bb97643c3acfc1f977a767dd9) | fix | correct Vitest coverage path resolution for JSDOM on Windows | +| [cdb607ada](https://github.com/angular/angular-cli/commit/cdb607ada4bf9aaec6ed8aafd8826d782fd13109) | fix | correctly configure per-browser headless mode in Vitest runner | +| [244931ece](https://github.com/angular/angular-cli/commit/244931ece877a1cacd1cfce64314e04a52526f80) | fix | correctly invoke `isTTY` as a function | +| [54d542738](https://github.com/angular/angular-cli/commit/54d542738e23c275ac6827f19da92213c405f9e2) | fix | ensure correct URL joining for prerender routes | +| [a28b38bbe](https://github.com/angular/angular-cli/commit/a28b38bbeba0977e99142a15d1ecc77c15abc416) | fix | force dev-server to use HTTP/1.1 when using SSR with SSL | +| [59ff867f0](https://github.com/angular/angular-cli/commit/59ff867f0d2e7f7f88480deefa0ee470c037197a) | fix | normalize `--include` paths to posix | ### @angular/ssr -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | -| [3a28fb6a1](https://github.com/angular/angular-cli/commit/3a28fb6a13061215b881c49232db979fc3c2f641) | fix | correctly handle routes with matrix parameters | -| [5db6d6487](https://github.com/angular/angular-cli/commit/5db6d64870c7ce0b883722a07c828946b7d2217d) | fix | ensure server-side navigation triggers a redirect | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------ | +| [03e231216](https://github.com/angular/angular-cli/commit/03e231216d3b8ba0de81da53a446eff0c701658d) | fix | handle `X-Forwarded-Prefix` and `APP_BASE_HREF` in redirects | +| [3cac01882](https://github.com/angular/angular-cli/commit/3cac0188271175e12cc238c6610b542f3ae14db3) | fix | prevent redirect loop with encoded query parameters | - + + +# 21.0.0 (2025-11-19) + +## Breaking Changes + +### @angular/cli + +- The `ng` commands will no longer automatically detect and use `cnpm` as the package manager. As an alternative use the `.npmrc` file to ensure npm uses the cnpm registry. + +### @angular/build -# 21.0.0-next.8 (2025-10-15) +- - TypeScript versions older than 5.9 are no longer supported. +- The `javascriptEnabled` option for Less is no longer supported. Projects relying on inline JavaScript within Less files will need to refactor their stylesheets to remove this dependency. ### @schematics/angular | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------ | +| [e417c89f9](https://github.com/angular/angular-cli/commit/e417c89f9e9cfe0ce50ffbc72ef555793605aea1) | feat | Add `addTypeToClassName` option to relevant schematics | | [ede5e52bc](https://github.com/angular/angular-cli/commit/ede5e52bc701c42948bd98826cd4fa901350015c) | feat | add `include` option to jasmine-to-vitest schematic | +| [c119910f4](https://github.com/angular/angular-cli/commit/c119910f4505e280eea83ea6647b6d279a46f36d) | feat | add AGENTS.md support to ai-config schematic | | [d0d2a17b8](https://github.com/angular/angular-cli/commit/d0d2a17b8adb2c1ce6eee70494f5d2298622c40e) | feat | add Jasmine spy API transformations to jasmine-to-vitest schematic | | [e7d955bed](https://github.com/angular/angular-cli/commit/e7d955bedd5ca6957903cb73f8ebe06823a808da) | feat | add matcher transformations to jasmine-to-vitest schematic | | [629f5cb18](https://github.com/angular/angular-cli/commit/629f5cb181fee562645baf02b44ebb3b39f3fb06) | feat | add misc transformations to jasmine-to-vitest schematic | +| [4912f3990](https://github.com/angular/angular-cli/commit/4912f39906b11a3212f11d5a00d577e2a0bacab4) | feat | add Tailwind CSS option to application schematic and `ng new` | +| [2a518016d](https://github.com/angular/angular-cli/commit/2a518016d9585dd4d16f90102d5409459ebba024) | feat | Applications are zoneless by default | +| [2ffc527b1](https://github.com/angular/angular-cli/commit/2ffc527b1bbe237e9f732d3506ce3a75ca1ca9d0) | feat | configure Vitest for new projects and allow runner choice | | [58474ec7d](https://github.com/angular/angular-cli/commit/58474ec7dd85fc34639c138d9b8d545affb50e3e) | feat | introduce initial jasmine-to-vitest unit test refactor schematic | +| [9f255f2b3](https://github.com/angular/angular-cli/commit/9f255f2b3cc435f3bea2f0266a137176ca599aef) | feat | set `packageManager` in `package.json` on new projects | +| [4e6c94f21](https://github.com/angular/angular-cli/commit/4e6c94f21e882c593cf11197900c29d693af9297) | feat | support different file name style guides in `ng new` | +| [77741f5ee](https://github.com/angular/angular-cli/commit/77741f5eec735f23b0f2865101471e045e4889b8) | fix | add 'update-typescript-lib' migration | +| [f89750b27](https://github.com/angular/angular-cli/commit/f89750b27866c307da546fe4f33da980693ca5c1) | fix | add `addImports` option to jasmine-vitest schematic | +| [9dab5780a](https://github.com/angular/angular-cli/commit/9dab5780a1befbd76ee9ba4c4e6ac2d3fd714bb9) | fix | add fixture.whenStable in spec files when zoneless apps | | [8f0f6a5f1](https://github.com/angular/angular-cli/commit/8f0f6a5f113ffc9e81d99eeeba71f8054e2d3686) | fix | add migration to update `moduleResolution` to `bundler` | +| [e8feba9ee](https://github.com/angular/angular-cli/commit/e8feba9ee163f688c51d6463336474591e886647) | fix | add missing typeSeparator to main.ts.template file | +| [515b09c4f](https://github.com/angular/angular-cli/commit/515b09c4f28ef1c2eb911cb73135a6086dcab3c9) | fix | add Vitest config generation and runner checks | +| [0e83fe1a8](https://github.com/angular/angular-cli/commit/0e83fe1a87cc3dcbc9daa4440a050ae6aafc8042) | fix | add warnings and improve Karma config generation | +| [b91fa31f2](https://github.com/angular/angular-cli/commit/b91fa31f20b49ead021c72c271f67da38b340584) | fix | align Karma project generation with unified unit-test builder | +| [c967a447c](https://github.com/angular/angular-cli/commit/c967a447ce755fbf582ec35aa24bb6e0fa0043cf) | fix | correct spacing in application spec tsconfig | +| [00d941c43](https://github.com/angular/angular-cli/commit/00d941c433de718cf3c38033d5d68dd86f790291) | fix | correct style guide paths for standalone components | +| [e33e77d12](https://github.com/angular/angular-cli/commit/e33e77d12984446fe7bc77deb7438806817ba8a7) | fix | flag '--file-name-style-guide=2016' - wrong import in main.ts | | [f35b9f331](https://github.com/angular/angular-cli/commit/f35b9f3310995b05d501f2abaec58dcd283e3aa0) | fix | improve comment preservation in jasmine-to-vitest | +| [6615fcf03](https://github.com/angular/angular-cli/commit/6615fcf037686cd96e97b469937b7f0736afaa77) | fix | issues in apps generated with '--file-name-style-guide=2016' flag | +| [e304821d5](https://github.com/angular/angular-cli/commit/e304821d5d789fab2725d3152612d3e5b6bd0dc7) | fix | make ai-config schematic non-destructive | +| [512ad282a](https://github.com/angular/angular-cli/commit/512ad282aecbfdf1e5c9e9700cc722addb949b68) | fix | preserve blank lines in jasmine-to-vitest schematic | +| [b524ba426](https://github.com/angular/angular-cli/commit/b524ba42625cd690177a300ca4843ef4edce035f) | fix | remove empty i18n-extract target for new projects | +| [8e6e0a293](https://github.com/angular/angular-cli/commit/8e6e0a2931bfb178e77cf2c9ca7f92a56c673449) | fix | remove explicit flag for host bindings | +| [afb4d3e37](https://github.com/angular/angular-cli/commit/afb4d3e377b11315a03563cb8c143c35d37f113a) | fix | remove extra space before async in spec templates | +| [b983ea8e5](https://github.com/angular/angular-cli/commit/b983ea8e5107420a910dbbc05c6b74f0ff6fbddd) | fix | respect skip-install for tailwind schematic | +| [54c4eae2a](https://github.com/angular/angular-cli/commit/54c4eae2aa49c6b45c41f0718a5915a10d426cb4) | fix | transform Jasmine type annotations in jasmine-to-vitest schematic | +| [14c0a9bac](https://github.com/angular/angular-cli/commit/14c0a9bacbb66b1db714ea7906c7d33f49c710fc) | perf | optimize AST traversal utilities | + +### @angular/cli + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------- | +| [58d101d5e](https://github.com/angular/angular-cli/commit/58d101d5e78cf4c158dbaf52103639d56b84f9ed) | feat | add `--json` output to `ng version` | +| [d014630fa](https://github.com/angular/angular-cli/commit/d014630fad765ae3928b698122038cbe00d37102) | feat | add advanced filtering to MCP example search | +| [6d3a3c579](https://github.com/angular/angular-cli/commit/6d3a3c5799bde1bab5c3878e0783ffa6854e36ad) | feat | add ai-tutor mcp tool | +| [1c06b16a9](https://github.com/angular/angular-cli/commit/1c06b16a962d3c2cc122dc40e01c64bc8a8d754d) | feat | add builder info to `list_projects` MCP tool | +| [301b50da4](https://github.com/angular/angular-cli/commit/301b50da4cf99b3cd87940606121d076b4f241c6) | feat | add fallback support for packages without direct `ng add` functionality | +| [3040b777e](https://github.com/angular/angular-cli/commit/3040b777e40bc90fd1ed961e3d134875b3f9b464) | feat | add style language detection to list_projects tool | +| [45024e836](https://github.com/angular/angular-cli/commit/45024e836b4006cc48b18bb99d025ae1a572db12) | feat | add unit test framework detection to list_projects tool | +| [104c90768](https://github.com/angular/angular-cli/commit/104c90768000b3e0052ee7e7de2c5e04c1bffdaf) | feat | enhance `ng version` output with more details | +| [286b6204c](https://github.com/angular/angular-cli/commit/286b6204c825c990761a0d5e5b91bb439dd13655) | feat | make documentation search tool version-aware | +| [406315d09](https://github.com/angular/angular-cli/commit/406315d0939c62d9f4f39ce64b168e72bbdd588c) | feat | make find_examples tool version-aware | +| [68e711307](https://github.com/angular/angular-cli/commit/68e711307eae88a621698c2a9cc2abc30d44efc8) | feat | make get_best_practices tool version-aware | +| [50453fdee](https://github.com/angular/angular-cli/commit/50453fdeec4a00d88deada49d2dd0867bdb784fb) | feat | overhaul `ng version` command output | +| [1ee9ce3c9](https://github.com/angular/angular-cli/commit/1ee9ce3c93caff419f8095a91cf58601e3df3f74) | feat | promote MCP `find_examples` tool to a stable tool | +| [0d53e82d5](https://github.com/angular/angular-cli/commit/0d53e82d5ed8986603c2005fc06041dd076b08c6) | feat | provide detailed peer dependency conflict errors in ng add | +| [f513089e2](https://github.com/angular/angular-cli/commit/f513089e276acf5a7c4f6879a95e2d6ed78ae67d) | feat | remove direct support for `cnpm` | +| [c17d7a929](https://github.com/angular/angular-cli/commit/c17d7a929adccb77f3c2c33e70005f50032d8cae) | fix | add schema versioning and metadata to example database | +| [dbf1aaf70](https://github.com/angular/angular-cli/commit/dbf1aaf70bc3e3dd0de05d760bafacc43b34dce8) | fix | add snippet support to example search MCP tool | +| [dfb4242b3](https://github.com/angular/angular-cli/commit/dfb4242b347365f3a2c6d006f07a16c982ff4dbe) | fix | add vitest to version command output | +| [11cee1acb](https://github.com/angular/angular-cli/commit/11cee1acb59afbad1ef88d8340b5438f7dbefe57) | fix | correct boolean parsing in MCP example front matter | +| [122a8c0e2](https://github.com/angular/angular-cli/commit/122a8c0e27342db79eb4d38e23032548054709b9) | fix | correct frontmatter parsing in MCP examples tool | +| [431106559](https://github.com/angular/angular-cli/commit/431106559d6e75f5113876a3f92fdf4dc4b2114d) | fix | correct query in find_examples to prevent runtime error | +| [def412a55](https://github.com/angular/angular-cli/commit/def412a558d71cb51fa16d826418bd0ed0a085cf) | fix | enhance find_examples MCP tool with structured output | +| [0922a033f](https://github.com/angular/angular-cli/commit/0922a033f546b38f83d1cae524cf7237dd37a2ac) | fix | improve JSON schema parsing for command options | +| [f099c9157](https://github.com/angular/angular-cli/commit/f099c91570b3cd748d7138bd18a4898a345549db) | fix | improve list_projects MCP tool to find all workspaces in monorepos | +| [1be35b343](https://github.com/angular/angular-cli/commit/1be35b3433179481be85ea1cb892d66170e0aebe) | fix | promote zoneless migration MCP tool to stable | +| [e5aed6d65](https://github.com/angular/angular-cli/commit/e5aed6d655ed92ea6eb3ac03716b8a02a5f731d6) | fix | show planned actions in `ng add` dry run | +| [4deac3ec7](https://github.com/angular/angular-cli/commit/4deac3ec785b1a53156aac90441d0ed129df71ef) | fix | support multi-database search in find_examples MCP tool | +| [aeb49dd52](https://github.com/angular/angular-cli/commit/aeb49dd52bf88785a193fcb6caa0b36aaeef1d37) | perf | cache dependency lookups during `ng add` | +| [5e534090e](https://github.com/angular/angular-cli/commit/5e534090e25e00a9fafbce2867030e7fdb0efbf6) | perf | parallelize peer dependency checks in `ng add` | ### @angular-devkit/build-angular | Commit | Type | Description | | --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | +| [6e395fc0c](https://github.com/angular/angular-cli/commit/6e395fc0c4505dd32b3237ea116e8db6bde25758) | fix | ensure vitest code coverage handles virtual files correctly | | [53899511a](https://github.com/angular/angular-cli/commit/53899511afe5665541984085914a313390af6ce2) | fix | expand `jest` and `jest-environment-jsdom` to allow version 30 | +| [7a8c94615](https://github.com/angular/angular-cli/commit/7a8c94615164e114533fae0f84796a374dc1b47b) | fix | make zone.js optional in server and app-shell builders | ### @angular/build -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- | -| [a90bea5b5](https://github.com/angular/angular-cli/commit/a90bea5b51c6978441919ed2af85c090fe99fd38) | feat | support `.test.ts` files by default in unit test builder | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------- | +| [00426e315](https://github.com/angular/angular-cli/commit/00426e3150c846913a5aa31510b5a1126df9e570) | feat | add --list-tests flag to unit-test builder | +| [a908bf3d4](https://github.com/angular/angular-cli/commit/a908bf3d4e8a59f3546f117bcc1f12fd69ad2d0b) | feat | add 'filter' option to unit-test builder | +| [3e0209d0a](https://github.com/angular/angular-cli/commit/3e0209d0a6bc6d7985d6294fc1430cdbe4d2d9a6) | feat | add `browserViewport` option for vitest browser tests | +| [3b7dabbf1](https://github.com/angular/angular-cli/commit/3b7dabbf1df9b2b6ca9ffc6c038abb6e40b6df2b) | feat | add advanced coverage options to unit-test builder | +| [c0b00d78e](https://github.com/angular/angular-cli/commit/c0b00d78ec37426f4474f473ddf9e627a0dd23df) | feat | add reporter output file option for unit-test | +| [66dd6dd83](https://github.com/angular/angular-cli/commit/66dd6dd835d6b489e6b4be2138aa443e11bfa076) | feat | allow options for unit test reporters | +| [a90bea5b5](https://github.com/angular/angular-cli/commit/a90bea5b51c6978441919ed2af85c090fe99fd38) | feat | support `.test.ts` files by default in unit test builder | +| [b2f048773](https://github.com/angular/angular-cli/commit/b2f048773c6014022983e7ccc52cb760619d8a1b) | fix | add --ui option for Vitest runner | +| [530d9270e](https://github.com/angular/angular-cli/commit/530d9270e87786594dd8d1956b0806a28650db25) | fix | add `define` option to dev-server | +| [b554bd73a](https://github.com/angular/angular-cli/commit/b554bd73a9c248d986ed718028edf52ab5da6ccf) | fix | add temporary directory cleanup for Vitest executor | +| [c6176f6df](https://github.com/angular/angular-cli/commit/c6176f6dffdae5c8d8708a1dd20fb51ca72e3c24) | fix | add upfront dependency validation for unit-test runners | +| [63c98741a](https://github.com/angular/angular-cli/commit/63c98741adcd21701b8bc572e9410cc1cf4f91c3) | fix | add webcontainer support for Vitest browser provider | +| [fcdbf6c19](https://github.com/angular/angular-cli/commit/fcdbf6c19b5a8549011aebec9e517feb12a99895) | fix | allow `globals` to be set to false | +| [542d52868](https://github.com/angular/angular-cli/commit/542d528683cc0e51fd5a55ed6dbf43168f7a51a5) | fix | allow custom runner configuration file for unit-test | +| [0505f954d](https://github.com/angular/angular-cli/commit/0505f954dcf3b3339749ff461592d46d8ecc5e23) | fix | allow unit-test progress option passthrough for building | +| [931c62d20](https://github.com/angular/angular-cli/commit/931c62d20915c6c772b61d76ab88657c0858f6bd) | fix | allow unit-test runner config with absolute path | +| [a11dd31f0](https://github.com/angular/angular-cli/commit/a11dd31f0c80a189e7dcb3172c89a731cfc34702) | fix | configure Vitest cache to use Angular cache | +| [abf003268](https://github.com/angular/angular-cli/commit/abf003268c6cb18f0944665b0b3f2794c9469c3e) | fix | correct Vitest builder watch mode execution | +| [f05ffd104](https://github.com/angular/angular-cli/commit/f05ffd104255e86fe93f3736e1430f940cb83007) | fix | correct Vitest coverage include handling for virtual files | +| [cd5c92b99](https://github.com/angular/angular-cli/commit/cd5c92b99a5d8e9cb991a2551f564353c3df0fbe) | fix | correct Vitest coverage reporting for test files | +| [07f712253](https://github.com/angular/angular-cli/commit/07f712253bb6c37d27ae7be9845f497002b0780c) | fix | correctly handle absolute paths and casing in test discovery | +| [bf468e1eb](https://github.com/angular/angular-cli/commit/bf468e1eb1050c60359b6f52692ce0db286982c2) | fix | direct check include file exists in unit-test discovery | +| [50e330d33](https://github.com/angular/angular-cli/commit/50e330d331fc8cfc4c12f7258012305ecb419d2d) | fix | disable glob directory expansion when finding tests | +| [49b65aba8](https://github.com/angular/angular-cli/commit/49b65aba8d7cd2839776e987366b981d7762760c) | fix | disable Vitest test isolation by default | +| [1529595d4](https://github.com/angular/angular-cli/commit/1529595d4a8d8ff9251d1680b1a23bf4ef817db0) | fix | drop support for TypeScript 5.8 | +| [a44f8fa94](https://github.com/angular/angular-cli/commit/a44f8fa94bbf6ce8cdee05552dc56124507c6971) | fix | dynamically select Vitest DOM environment | +| [ae35543af](https://github.com/angular/angular-cli/commit/ae35543af7f5b3a5328c39fd4617d61b48067357) | fix | enhance Vitest config merging and validation | +| [fec106b60](https://github.com/angular/angular-cli/commit/fec106b60553394aab51d713e5437a713085089b) | fix | enhance Vitest dependency externalization and pre-bundling | +| [f7c4a4c1d](https://github.com/angular/angular-cli/commit/f7c4a4c1dcd575dec82cc06597e3d6620b1be729) | fix | enhance Vitest resolution for optimal package loading | +| [ee5e127d5](https://github.com/angular/angular-cli/commit/ee5e127d551269fa9a3e39b9b28e38d7ab35806f) | fix | ensure `ɵgetOrCreateAngularServerApp` is always defined after errors | +| [0830f4fb5](https://github.com/angular/angular-cli/commit/0830f4fb549e2c45b1ef752dd42f002a1347d7c8) | fix | ensure TestBed cleanup hooks are always registered | +| [41b12509a](https://github.com/angular/angular-cli/commit/41b12509a9db8bca637e0c67d21301a75774129c) | fix | ensure TestBed setup is robust in non-isolated Vitest | +| [55145f582](https://github.com/angular/angular-cli/commit/55145f582253b4ecb47add7ff2ef459b7535dfdb) | fix | ensure Vitest setup files are executed in order | +| [3478aa332](https://github.com/angular/angular-cli/commit/3478aa332ef0241c04e7eeef9dd74b017292b2c4) | fix | exclude .angular from coverage instrumentation | +| [7c529c1bc](https://github.com/angular/angular-cli/commit/7c529c1bc606101ab8c506e0b7845d2e9f509db4) | fix | externalize Angular dependencies in Vitest runner | +| [69c3b1226](https://github.com/angular/angular-cli/commit/69c3b1226880835fd8087cea5684ababb92b1c05) | fix | improve error handling in unit-test builder | +| [bab5806c2](https://github.com/angular/angular-cli/commit/bab5806c281fd4cdd63b7969e691d703ed1e7680) | fix | introduce vitest-base.config for test configuration | +| [73621998f](https://github.com/angular/angular-cli/commit/73621998f91db189ad9b1ab006681404e30f7900) | fix | normalize paths for Vitest runner output files | +| [fa5c92346](https://github.com/angular/angular-cli/commit/fa5c92346d14a6ad03aa30ad6392fc649038605e) | fix | prioritize string type for runnerConfig schema | +| [d0787c11d](https://github.com/angular/angular-cli/commit/d0787c11d68841c36ef28bc3f15963406d1209a9) | fix | provide default excludes for vitest coverage | +| [ac10f323e](https://github.com/angular/angular-cli/commit/ac10f323ece9f4a35068e510f10786fbcb15adbb) | fix | relax requirement for files to be in TS compilation | +| [139758586](https://github.com/angular/angular-cli/commit/13975858683421a5712bbfccee57cf141a0b96f6) | fix | remove deprecated `javascriptEnabled` option for Less | +| [6576bb598](https://github.com/angular/angular-cli/commit/6576bb5985c18dca7cecd9509939c2a78bf9758a) | fix | remove explicit test isolation configuration | +| [9132e6af9](https://github.com/angular/angular-cli/commit/9132e6af9fd573d8b39c69a50b4b93e256145fd4) | fix | resolve browser provider packages using project resolver | +| [26127bd3b](https://github.com/angular/angular-cli/commit/26127bd3bb2c4b9aacf2a8f4c2cbdf732512bafb) | fix | resolve PostCSS plugins relative to config file | +| [dae732059](https://github.com/angular/angular-cli/commit/dae732059d17e9e374ac7635fbca9480751f70b3) | fix | serve build assets and styles in vitest | +| [705af2278](https://github.com/angular/angular-cli/commit/705af22788102eeade08404d357582c39de8900b) | fix | set coverage report directory to coverage/project-name | +| [0851d2eae](https://github.com/angular/angular-cli/commit/0851d2eae1e5b854a0a8a7df3a47b00693508a0f) | fix | show full aggregate errors from vitest | +| [cc2668f57](https://github.com/angular/angular-cli/commit/cc2668f5744588f9c3d847d2450dd1361e73c690) | fix | simplify SSL handling for `ng serve` with SSR ([#31723](https://github.com/angular/angular-cli/pull/31723)) | +| [907eabdd3](https://github.com/angular/angular-cli/commit/907eabdd3c7447ed2c211b6d6c2719b04443c545) | fix | support ESM PostCSS plugins | +| [62938e799](https://github.com/angular/angular-cli/commit/62938e79977d14045b7883d459d786dbb8d4d7ee) | fix | update vitest to 4.0.6 and remove coverage workaround | + + + + + +# 20.3.7 (2025-10-22) + +### @angular-devkit/schematics + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------------- | +| [a31533cf4](https://github.com/angular/angular-cli/commit/a31533cf492048f62a41b9c09e53779269ee172d) | fix | respect `--force` option when schematic contains `host.create` | + +### @angular/build + +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------ | +| [8cdda111c](https://github.com/angular/angular-cli/commit/8cdda111cc0b343aa5eb6a7ccbad93302a543226) | fix | resolve Angular locale data namespace in esbuild | +| [5847ccc54](https://github.com/angular/angular-cli/commit/5847ccc545e54eb77a78b2435db7970faf748156) | fix | update `vite` to `7.11.1` | ### @angular/ssr -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------ | -| [7be6c8f0e](https://github.com/angular/angular-cli/commit/7be6c8f0e2883c85546eb1691c91fa7d4aefc0d3) | fix | prevent malicious URL from overriding host | +| Commit | Type | Description | +| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | +| [3a28fb6a1](https://github.com/angular/angular-cli/commit/3a28fb6a13061215b881c49232db979fc3c2f641) | fix | correctly handle routes with matrix parameters | +| [5db6d6487](https://github.com/angular/angular-cli/commit/5db6d64870c7ce0b883722a07c828946b7d2217d) | fix | ensure server-side navigation triggers a redirect | @@ -84,33 +245,6 @@ - - -# 21.0.0-next.7 (2025-10-08) - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------------------------------- | -| [1c06b16a9](https://github.com/angular/angular-cli/commit/1c06b16a962d3c2cc122dc40e01c64bc8a8d754d) | feat | add builder info to `list_projects` MCP tool | -| [104c90768](https://github.com/angular/angular-cli/commit/104c90768000b3e0052ee7e7de2c5e04c1bffdaf) | feat | enhance `ng version` output with more details | - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- | -| [afb4d3e37](https://github.com/angular/angular-cli/commit/afb4d3e377b11315a03563cb8c143c35d37f113a) | fix | remove extra space before async in spec templates | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- | -| [1c2d49ec7](https://github.com/angular/angular-cli/commit/1c2d49ec736818d22773916d7eaafd3446275ea0) | fix | cleanup karma temporary directory after process exit | -| [50e330d33](https://github.com/angular/angular-cli/commit/50e330d331fc8cfc4c12f7258012305ecb419d2d) | fix | disable glob directory expansion when finding tests | -| [73621998f](https://github.com/angular/angular-cli/commit/73621998f91db189ad9b1ab006681404e30f7900) | fix | normalize paths for Vitest runner output files | - - - # 20.3.5 (2025-10-08) @@ -123,46 +257,6 @@ - - -# 21.0.0-next.6 (2025-10-03) - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------- | -| [58d101d5e](https://github.com/angular/angular-cli/commit/58d101d5e78cf4c158dbaf52103639d56b84f9ed) | feat | add `--json` output to `ng version` | -| [50453fdee](https://github.com/angular/angular-cli/commit/50453fdeec4a00d88deada49d2dd0867bdb784fb) | feat | overhaul `ng version` command output | -| [0922a033f](https://github.com/angular/angular-cli/commit/0922a033f546b38f83d1cae524cf7237dd37a2ac) | fix | improve JSON schema parsing for command options | - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [c119910f4](https://github.com/angular/angular-cli/commit/c119910f4505e280eea83ea6647b6d279a46f36d) | feat | add AGENTS.md support to ai-config schematic | -| [9dab5780a](https://github.com/angular/angular-cli/commit/9dab5780a1befbd76ee9ba4c4e6ac2d3fd714bb9) | fix | add fixture.whenStable in spec files when zoneless apps | -| [e304821d5](https://github.com/angular/angular-cli/commit/e304821d5d789fab2725d3152612d3e5b6bd0dc7) | fix | make ai-config schematic non-destructive | -| [8ac515699](https://github.com/angular/angular-cli/commit/8ac515699cfd5a4e7eda9bcc054dfd7c68faba39) | fix | Out of the box support for PM2 | -| [57075a31a](https://github.com/angular/angular-cli/commit/57075a31ad8f95a82304fe8533b3bca828d8da42) | fix | use bracket notation for `process.env['pm_id']` | - -### @angular-devkit/build-angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [acd785afc](https://github.com/angular/angular-cli/commit/acd785afc956efad56b03402085ff94855b9fcc6) | fix | mark `InjectionToken` as pure for improved tree-shaking | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [3e0209d0a](https://github.com/angular/angular-cli/commit/3e0209d0a6bc6d7985d6294fc1430cdbe4d2d9a6) | feat | add `browserViewport` option for vitest browser tests | -| [3b7dabbf1](https://github.com/angular/angular-cli/commit/3b7dabbf1df9b2b6ca9ffc6c038abb6e40b6df2b) | feat | add advanced coverage options to unit-test builder | -| [65562114c](https://github.com/angular/angular-cli/commit/65562114cdf725fa52f6d805f26a1aa265b9badb) | fix | mark `InjectionToken` as pure for improved tree-shaking | -| [d0787c11d](https://github.com/angular/angular-cli/commit/d0787c11d68841c36ef28bc3f15963406d1209a9) | fix | provide default excludes for vitest coverage | -| [ac10f323e](https://github.com/angular/angular-cli/commit/ac10f323ece9f4a35068e510f10786fbcb15adbb) | fix | relax requirement for files to be in TS compilation | - - - # 20.3.4 (2025-10-02) @@ -188,55 +282,6 @@ - - -# 21.0.0-next.5 (2025-09-24) - -## Breaking Changes - -### @angular/build - -- The `javascriptEnabled` option for Less is no longer supported. Projects relying on inline JavaScript within Less files will need to refactor their stylesheets to remove this dependency. - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [2a518016d](https://github.com/angular/angular-cli/commit/2a518016d9585dd4d16f90102d5409459ebba024) | feat | Applications are zoneless by default | -| [9f255f2b3](https://github.com/angular/angular-cli/commit/9f255f2b3cc435f3bea2f0266a137176ca599aef) | feat | set `packageManager` in `package.json` on new projects | -| [77741f5ee](https://github.com/angular/angular-cli/commit/77741f5eec735f23b0f2865101471e045e4889b8) | fix | add 'update-typescript-lib' migration | -| [3af4dcbbf](https://github.com/angular/angular-cli/commit/3af4dcbbf4019e13a9547a404516502cf4eda736) | fix | add `__screenshots__/` to `.gitignore` | - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | --------------------- | -| [6d3a3c579](https://github.com/angular/angular-cli/commit/6d3a3c5799bde1bab5c3878e0783ffa6854e36ad) | feat | add ai-tutor mcp tool | - -### @angular-devkit/build-angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [7a8c94615](https://github.com/angular/angular-cli/commit/7a8c94615164e114533fae0f84796a374dc1b47b) | fix | make zone.js optional in server and app-shell builders | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [00426e315](https://github.com/angular/angular-cli/commit/00426e3150c846913a5aa31510b5a1126df9e570) | feat | add --list-tests flag to unit-test builder | -| [3478aa332](https://github.com/angular/angular-cli/commit/3478aa332ef0241c04e7eeef9dd74b017292b2c4) | fix | exclude .angular from coverage instrumentation | -| [139758586](https://github.com/angular/angular-cli/commit/13975858683421a5712bbfccee57cf141a0b96f6) | fix | remove deprecated `javascriptEnabled` option for Less | -| [705af2278](https://github.com/angular/angular-cli/commit/705af22788102eeade08404d357582c39de8900b) | fix | set coverage report directory to coverage/project-name | -| [907eabdd3](https://github.com/angular/angular-cli/commit/907eabdd3c7447ed2c211b6d6c2719b04443c545) | fix | support ESM PostCSS plugins | - -### @angular/ssr - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------- | -| [afa273849](https://github.com/angular/angular-cli/commit/afa273849d0e0e62a7df2236d818bf7800c3ad13) | fix | avoid retaining rendered HTML in memory post-request | - - - # 20.3.3 (2025-09-24) @@ -255,24 +300,6 @@ - - -# 21.0.0-next.4 (2025-09-17) - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [a908bf3d4](https://github.com/angular/angular-cli/commit/a908bf3d4e8a59f3546f117bcc1f12fd69ad2d0b) | feat | add 'filter' option to unit-test builder | -| [c0b00d78e](https://github.com/angular/angular-cli/commit/c0b00d78ec37426f4474f473ddf9e627a0dd23df) | feat | add reporter output file option for unit-test | -| [66dd6dd83](https://github.com/angular/angular-cli/commit/66dd6dd835d6b489e6b4be2138aa443e11bfa076) | feat | allow options for unit test reporters | -| [43fc5536f](https://github.com/angular/angular-cli/commit/43fc5536fd42694a09a7b7c25fe8c5665e3e28d3) | fix | add timestamp to bundle generation log | -| [c6176f6df](https://github.com/angular/angular-cli/commit/c6176f6dffdae5c8d8708a1dd20fb51ca72e3c24) | fix | add upfront dependency validation for unit-test runners | -| [69c3b1226](https://github.com/angular/angular-cli/commit/69c3b1226880835fd8087cea5684ababb92b1c05) | fix | improve error handling in unit-test builder | -| [dae732059](https://github.com/angular/angular-cli/commit/dae732059d17e9e374ac7635fbca9480751f70b3) | fix | serve build assets and styles in vitest | - - - # 20.3.2 (2025-09-17) @@ -390,75 +417,6 @@ - - -# 21.0.0-next.3 (2025-09-10) - -## Breaking Changes - -### @angular/build - -- - TypeScript versions older than 5.9 are no longer supported. - -### @angular/ssr - -- The server-side bootstrapping process has been changed to eliminate the reliance on a global platform injector. - - Before: - - ```ts - const bootstrap = () => bootstrapApplication(AppComponent, config); - ``` - - After: - - ```ts - const bootstrap = (context: BootstrapContext) => - bootstrapApplication(AppComponent, config, context); - ``` - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------- | -| [ddebe3d4f](https://github.com/angular/angular-cli/commit/ddebe3d4fc35486a57f4051fdd4493caba4e6c07) | fix | align labels in ai-config schema | -| [8e6e0a293](https://github.com/angular/angular-cli/commit/8e6e0a2931bfb178e77cf2c9ca7f92a56c673449) | fix | remove explicit flag for host bindings | -| [b983ea8e5](https://github.com/angular/angular-cli/commit/b983ea8e5107420a910dbbc05c6b74f0ff6fbddd) | fix | respect skip-install for tailwind schematic | - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------- | -| [d014630fa](https://github.com/angular/angular-cli/commit/d014630fad765ae3928b698122038cbe00d37102) | feat | add advanced filtering to MCP example search | -| [1ee9ce3c9](https://github.com/angular/angular-cli/commit/1ee9ce3c93caff419f8095a91cf58601e3df3f74) | feat | promote MCP `find_examples` tool to a stable tool | -| [dbf1aaf70](https://github.com/angular/angular-cli/commit/dbf1aaf70bc3e3dd0de05d760bafacc43b34dce8) | fix | add snippet support to example search MCP tool | -| [11cee1acb](https://github.com/angular/angular-cli/commit/11cee1acb59afbad1ef88d8340b5438f7dbefe57) | fix | correct boolean parsing in MCP example front matter | -| [def412a55](https://github.com/angular/angular-cli/commit/def412a558d71cb51fa16d826418bd0ed0a085cf) | fix | enhance find_examples MCP tool with structured output | -| [2037b912b](https://github.com/angular/angular-cli/commit/2037b912b2f78eb4469d8671fbca8c43f06cd2ff) | fix | improve bun lockfile detection and optimize lockfile checks | - -### @angular-devkit/build-angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------ | -| [9749ec687](https://github.com/angular/angular-cli/commit/9749ec687800c1bbeae4b75550dee3608bbe6823) | fix | avoid extra tick in SSR builds | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------ | -| [cd5c92b99](https://github.com/angular/angular-cli/commit/cd5c92b99a5d8e9cb991a2551f564353c3df0fbe) | fix | correct Vitest coverage reporting for test files | -| [1529595d4](https://github.com/angular/angular-cli/commit/1529595d4a8d8ff9251d1680b1a23bf4ef817db0) | fix | drop support for TypeScript 5.8 | -| [58da860fc](https://github.com/angular/angular-cli/commit/58da860fc4e040d1dbce0b1955c361a2efdb3559) | fix | preserve names in esbuild for improved debugging in dev mode | -| [26127bd3b](https://github.com/angular/angular-cli/commit/26127bd3bb2c4b9aacf2a8f4c2cbdf732512bafb) | fix | resolve PostCSS plugins relative to config file | - -### @angular/ssr - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------- | -| [f0b0980fb](https://github.com/angular/angular-cli/commit/f0b0980fbd55473f152ec3b87fa5e1923c876854) | feat | introduce BootstrapContext for isolated server-side rendering | - - - # 20.3.0 (2025-09-10) @@ -514,36 +472,6 @@ - - -# 21.0.0-next.2 (2025-09-03) - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ----------------------------------------------------------------------- | -| [301b50da4](https://github.com/angular/angular-cli/commit/301b50da4cf99b3cd87940606121d076b4f241c6) | feat | add fallback support for packages without direct `ng add` functionality | -| [2c498d2b8](https://github.com/angular/angular-cli/commit/2c498d2b87c13a63bef2a9be2ca4f087c72c6b8a) | fix | don't set a default for array options when length is 0 | -| [f099c9157](https://github.com/angular/angular-cli/commit/f099c91570b3cd748d7138bd18a4898a345549db) | fix | improve list_projects MCP tool to find all workspaces in monorepos | -| [e192e8c7e](https://github.com/angular/angular-cli/commit/e192e8c7ecf506e4b03668f527de83f2a57f552d) | fix | set process title when running architect commands | - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [e417c89f9](https://github.com/angular/angular-cli/commit/e417c89f9e9cfe0ce50ffbc72ef555793605aea1) | feat | Add `addTypeToClassName` option to relevant schematics | -| [4e6c94f21](https://github.com/angular/angular-cli/commit/4e6c94f21e882c593cf11197900c29d693af9297) | feat | support different file name style guides in `ng new` | -| [14c0a9bac](https://github.com/angular/angular-cli/commit/14c0a9bacbb66b1db714ea7906c7d33f49c710fc) | perf | optimize AST traversal utilities | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------ | -| [7b0f69798](https://github.com/angular/angular-cli/commit/7b0f69798f061d5500620828cf304e05d667199f) | fix | avoid extra tick in SSR dev-server builds | -| [f806f6477](https://github.com/angular/angular-cli/commit/f806f6477af222907f1879181fb0f9839e889ea8) | fix | maintain media output hashing with vitest unit-testing | - - - # 20.2.2 (2025-09-03) @@ -564,52 +492,6 @@ - - -# 21.0.0-next.1 (2025-08-27) - -## Breaking Changes - -### @angular/cli - -- The `ng` commands will no longer automatically detect and use `cnpm` as the package manager. As an alternative use the `.npmrc` file to ensure npm uses the cnpm registry. - -### @angular-devkit/schematics-cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------- | -| [aed26c388](https://github.com/angular/angular-cli/commit/aed26c38803a465842ff128c3f81bd6984e1fe3d) | fix | correctly set default array values | - -### @schematics/angular - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------ | -| [4912f3990](https://github.com/angular/angular-cli/commit/4912f39906b11a3212f11d5a00d577e2a0bacab4) | feat | add Tailwind CSS option to application schematic and `ng new` | -| [6c7b79833](https://github.com/angular/angular-cli/commit/6c7b798332786d29070460669e093e37902c4438) | fix | directly resolve karma config template in migration | -| [0f86cf878](https://github.com/angular/angular-cli/commit/0f86cf8782d1c08d11bb9ee54a30fe1954dd8bcc) | fix | prevent AI config schematic from failing when 'none' and other AI tools are selected | - -### @angular/cli - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ---------------------------------------------------------- | -| [0d53e82d5](https://github.com/angular/angular-cli/commit/0d53e82d5ed8986603c2005fc06041dd076b08c6) | feat | provide detailed peer dependency conflict errors in ng add | -| [f513089e2](https://github.com/angular/angular-cli/commit/f513089e276acf5a7c4f6879a95e2d6ed78ae67d) | feat | remove direct support for `cnpm` | -| [47d77a3ed](https://github.com/angular/angular-cli/commit/47d77a3edea4dabb463d50c2bdba32475257d775) | fix | correctly set default array values | -| [e5aed6d65](https://github.com/angular/angular-cli/commit/e5aed6d655ed92ea6eb3ac03716b8a02a5f731d6) | fix | show planned actions in `ng add` dry run | -| [aeb49dd52](https://github.com/angular/angular-cli/commit/aeb49dd52bf88785a193fcb6caa0b36aaeef1d37) | perf | cache dependency lookups during `ng add` | -| [5e534090e](https://github.com/angular/angular-cli/commit/5e534090e25e00a9fafbce2867030e7fdb0efbf6) | perf | parallelize peer dependency checks in `ng add` | - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | ------------------------------------------------------- | -| [b554bd73a](https://github.com/angular/angular-cli/commit/b554bd73a9c248d986ed718028edf52ab5da6ccf) | fix | add temporary directory cleanup for Vitest executor | -| [261dbb37c](https://github.com/angular/angular-cli/commit/261dbb37cbe01492240c4cedc644663b15a4296a) | fix | correct JS/TS file paths when running under Bazel | -| [abf003268](https://github.com/angular/angular-cli/commit/abf003268c6cb18f0944665b0b3f2794c9469c3e) | fix | correct Vitest builder watch mode execution | -| [4b49a207a](https://github.com/angular/angular-cli/commit/4b49a207a1de27b82416c6225a59bc10f48bdcbc) | fix | ensure karma polyfills reporter factory returns a value | - - - # 20.2.1 (2025-08-27) @@ -642,18 +524,6 @@ - - -# 21.0.0-next.0 (2025-08-20) - -### @angular/build - -| Commit | Type | Description | -| --------------------------------------------------------------------------------------------------- | ---- | -------------------------------------------------------- | -| [0505f954d](https://github.com/angular/angular-cli/commit/0505f954dcf3b3339749ff461592d46d8ecc5e23) | fix | allow unit-test progress option passthrough for building | - - - # 20.2.0 (2025-08-20) diff --git a/MODULE.bazel b/MODULE.bazel index 126db7de4830..fada853c7a71 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -4,10 +4,10 @@ module( name = "angular_cli", ) -bazel_dep(name = "yq.bzl", version = "0.3.1") -bazel_dep(name = "rules_nodejs", version = "6.6.0") -bazel_dep(name = "aspect_rules_js", version = "2.7.0") -bazel_dep(name = "aspect_rules_ts", version = "3.7.0") +bazel_dep(name = "yq.bzl", version = "0.3.2") +bazel_dep(name = "rules_nodejs", version = "6.6.2") +bazel_dep(name = "aspect_rules_js", version = "2.8.2") +bazel_dep(name = "aspect_rules_ts", version = "3.7.1") bazel_dep(name = "rules_pkg", version = "0.8.1") # Alow for usage of rules_pkg@0.8.1 even though other deps want a later verison. @@ -21,19 +21,19 @@ multiple_version_override( bazel_dep(name = "aspect_bazel_lib", version = "2.21.2") bazel_dep(name = "bazel_skylib", version = "1.8.2") -bazel_dep(name = "aspect_rules_esbuild", version = "0.23.0") +bazel_dep(name = "aspect_rules_esbuild", version = "0.24.0") bazel_dep(name = "aspect_rules_jasmine", version = "2.0.0") bazel_dep(name = "rules_angular") git_override( module_name = "rules_angular", - commit = "c3721b6ece2050a59a97562e3b95527a3092b03b", + commit = "9b751f826628cab386d1e8ae9c1fa766dbc6a495", remote = "https://github.com/devversion/rules_angular.git", ) bazel_dep(name = "devinfra") git_override( module_name = "devinfra", - commit = "ab6a00e9a219c2169ae0540cc5a32be5f481e004", + commit = "95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae", remote = "https://github.com/angular/dev-infra.git", ) @@ -47,7 +47,7 @@ git_override( bazel_dep(name = "rules_browsers") git_override( module_name = "rules_browsers", - commit = "6a699bf3e896690e2923cf3ade29fbd4e492e366", + commit = "f066f614451374721fac787fb1f0dbbd818d50d4", remote = "https://github.com/devversion/rules_browsers.git", ) diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index d0532032bed1..71d6aa83b976 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -14,24 +14,26 @@ "https://bcr.bazel.build/modules/apple_support/1.23.1/source.json": "d888b44312eb0ad2c21a91d026753f330caa48a25c9b2102fae75eb2b0dcfdd2", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.0.0/MODULE.bazel": "e118477db5c49419a88d78ebc7a2c2cea9d49600fe0f490c1903324a2c16ecd9", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.14.0/MODULE.bazel": "2b31ffcc9bdc8295b2167e07a757dbbc9ac8906e7028e5170a3708cecaac119f", + "https://bcr.bazel.build/modules/aspect_bazel_lib/2.17.1/MODULE.bazel": "9b027af55f619c7c444cead71061578fab6587e5e1303fa4ed61d49d2b1a7262", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.19.3/MODULE.bazel": "253d739ba126f62a5767d832765b12b59e9f8d2bc88cc1572f4a73e46eb298ca", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/MODULE.bazel": "276347663a25b0d5bd6cad869252bea3e160c4d980e764b15f3bae7f80b30624", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.21.2/source.json": "f42051fa42629f0e59b7ac2adf0a55749144b11f1efcd8c697f0ee247181e526", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.7.7/MODULE.bazel": "491f8681205e31bb57892d67442ce448cda4f472a8e6b3dc062865e29a64f89c", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.8.1/MODULE.bazel": "812d2dd42f65dca362152101fbec418029cc8fd34cbad1a2fde905383d705838", "https://bcr.bazel.build/modules/aspect_bazel_lib/2.9.3/MODULE.bazel": "66baf724dbae7aff4787bf2245cc188d50cb08e07789769730151c0943587c14", - "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.23.0/MODULE.bazel": "9b437a9ec25a619304940434fa03b8d41248213eb7009da2c898f3d6a4075ef3", - "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.23.0/source.json": "7b4cac4e61bae4262e7f67f6bec0b200fcb9060044f12e84a3bc37e0be245de7", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.24.0/MODULE.bazel": "15d19e46ec74e9e49ddf3c335e7a91b0657571654b0960bdcd10b771eeb4f7cb", + "https://bcr.bazel.build/modules/aspect_rules_esbuild/0.24.0/source.json": "6cc8c0ba6c623527e383acfe4ee73f290eeead2431093668db3b7a579a948deb", "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.0/MODULE.bazel": "071d1952527721bf8b180e1299def24edaece9d7466e31a311981640da82c6be", "https://bcr.bazel.build/modules/aspect_rules_jasmine/2.0.0/source.json": "45fa9603cdfe100575a12d8b65fa425fe8713dd8c9f0cdf802168b670bc0e299", "https://bcr.bazel.build/modules/aspect_rules_js/2.0.0/MODULE.bazel": "b45b507574aa60a92796e3e13c195cd5744b3b8aff516a9c0cb5ae6a048161c5", "https://bcr.bazel.build/modules/aspect_rules_js/2.4.2/MODULE.bazel": "0d01db38b96d25df7ed952a5e96eac4b3802723d146961974bf020f6dd07591d", "https://bcr.bazel.build/modules/aspect_rules_js/2.6.2/MODULE.bazel": "ed2a871f4ab8fbde0cab67c425745069d84ea64b64313fa1a2954017326511f5", - "https://bcr.bazel.build/modules/aspect_rules_js/2.7.0/MODULE.bazel": "ac879ee86f124c827e4e87942b3797ff4aaf90360eb9d7bff5321fc45d5ebefb", - "https://bcr.bazel.build/modules/aspect_rules_js/2.7.0/source.json": "20c34042beca8ea1e5996989dc163a75cb04ec4e75dc64f78d936497aea78e4b", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.2/MODULE.bazel": "76526405d6a65dae45db16b8b4619b502063ac573d2a2ae0a90fddc7d3247288", + "https://bcr.bazel.build/modules/aspect_rules_js/2.8.2/source.json": "a03164e3f59a05d9a6d205a477ea49ba8ee141ab764a9f38b43e6302eb4fa2b9", "https://bcr.bazel.build/modules/aspect_rules_ts/3.6.3/MODULE.bazel": "d09db394970f076176ce7bab5b5fa7f0d560fd4f30b8432ea5e2c2570505b130", "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.0/MODULE.bazel": "5aace216caf88638950ef061245d23c36f57c8359e56e97f02a36f70bb09c50f", - "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.0/source.json": "4a8115ea69dd796353232ff27a7e93e6d7d1ad43bea1eb33c6bd3acfa656bf2e", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.1/MODULE.bazel": "cbed416847e2c46c4c0fe275e3a3c8e302d236d0fb04a094e9af82d14e7c5040", + "https://bcr.bazel.build/modules/aspect_rules_ts/3.7.1/source.json": "7914a860fdf6ac399a3142fee2579184f0810fe0b7dee2eb9216ab72e6d8864e", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.3/MODULE.bazel": "20f53b145f40957a51077ae90b37b7ce83582a1daf9350349f0f86179e19dd0d", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.6/MODULE.bazel": "cafb8781ad591bc57cc765dca5fefab08cf9f65af363d162b79d49205c7f8af7", "https://bcr.bazel.build/modules/aspect_tools_telemetry/0.2.8/MODULE.bazel": "aa975a83e72bcaac62ee61ab12b788ea324a1d05c4aab28aadb202f647881679", @@ -49,7 +51,8 @@ "https://bcr.bazel.build/modules/bazel_features/1.4.1/MODULE.bazel": "e45b6bb2350aff3e442ae1111c555e27eac1d915e77775f6fdc4b351b758b5d7", "https://bcr.bazel.build/modules/bazel_features/1.9.0/MODULE.bazel": "885151d58d90d8d9c811eb75e3288c11f850e1d6b481a8c9f766adee4712358b", "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/MODULE.bazel": "407729e232f611c3270005b016b437005daa7b1505826798ea584169a476e878", - "https://bcr.bazel.build/modules/bazel_lib/3.0.0-beta.1/source.json": "72bfbe19a3936675719157798de64631e9ac54c2b41f13b544b821d094f4840a", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0/MODULE.bazel": "22b70b80ac89ad3f3772526cd9feee2fa412c2b01933fea7ed13238a448d370d", + "https://bcr.bazel.build/modules/bazel_lib/3.0.0/source.json": "895f21909c6fba01d7c17914bb6c8e135982275a1b18cdaa4e62272217ef1751", "https://bcr.bazel.build/modules/bazel_skylib/1.0.3/MODULE.bazel": "bcb0fd896384802d1ad283b4e4eb4d718eebd8cb820b0a2c3a347fb971afd9d8", "https://bcr.bazel.build/modules/bazel_skylib/1.1.1/MODULE.bazel": "1add3e7d93ff2e6998f9e118022c84d163917d912f5afafb3058e3d2f1545b5e", "https://bcr.bazel.build/modules/bazel_skylib/1.2.0/MODULE.bazel": "44fe84260e454ed94ad326352a698422dbe372b21a1ac9f3eab76eb531223686", @@ -153,8 +156,8 @@ "https://bcr.bazel.build/modules/rules_nodejs/6.3.0/MODULE.bazel": "45345e4aba35dd6e4701c1eebf5a4e67af4ed708def9ebcdc6027585b34ee52d", "https://bcr.bazel.build/modules/rules_nodejs/6.5.0/MODULE.bazel": "546d0cf79f36f9f6e080816045f97234b071c205f4542e3351bd4424282a8810", "https://bcr.bazel.build/modules/rules_nodejs/6.5.2/MODULE.bazel": "7f9ea68a0ce6d82905ce9f74e76ab8a8b4531ed4c747018c9d76424ad0b3370d", - "https://bcr.bazel.build/modules/rules_nodejs/6.6.0/MODULE.bazel": "49ef4cccc17a5a13c9beca2d0b3f7dea1d97381df8cb6ba4dea03943f6a81b0a", - "https://bcr.bazel.build/modules/rules_nodejs/6.6.0/source.json": "cbd156fa2db33707275de138110b78eb86a4cf510b979df7c4b6a8e7127484de", + "https://bcr.bazel.build/modules/rules_nodejs/6.6.2/MODULE.bazel": "9fdb5e1d50246a25761f150fcc820dc47e4052330a8408451e628804f9ca64a6", + "https://bcr.bazel.build/modules/rules_nodejs/6.6.2/source.json": "6e8c1ecc64ff8da147c1620f862ad77d7b19c5d1b52b3aa5e847d5b3d0de4cc3", "https://bcr.bazel.build/modules/rules_pkg/0.7.0/MODULE.bazel": "df99f03fc7934a4737122518bb87e667e62d780b610910f0447665a7e2be62dc", "https://bcr.bazel.build/modules/rules_pkg/0.8.1/MODULE.bazel": "7e9e7b5b26bd7ff012dfe63930db2f0176ddcd25e44a858fc72d63e995b6aab9", "https://bcr.bazel.build/modules/rules_pkg/0.8.1/source.json": "15dd7e13dc303f7fcde2b55300bcb8de5c0dd08a7a7269749cbbaa0fb1dfbe16", @@ -190,13 +193,13 @@ "https://bcr.bazel.build/modules/stardoc/0.7.2/source.json": "58b029e5e901d6802967754adf0a9056747e8176f017cfe3607c0851f4d42216", "https://bcr.bazel.build/modules/tar.bzl/0.2.1/MODULE.bazel": "52d1c00a80a8cc67acbd01649e83d8dd6a9dc426a6c0b754a04fe8c219c76468", "https://bcr.bazel.build/modules/tar.bzl/0.5.1/MODULE.bazel": "7c2eb3dcfc53b0f3d6f9acdfd911ca803eaf92aadf54f8ca6e4c1f3aee288351", - "https://bcr.bazel.build/modules/tar.bzl/0.6.0/MODULE.bazel": "a3584b4edcfafcabd9b0ef9819808f05b372957bbdff41601429d5fd0aac2e7c", - "https://bcr.bazel.build/modules/tar.bzl/0.6.0/source.json": "4a620381df075a16cb3a7ed57bd1d05f7480222394c64a20fa51bdb636fda658", + "https://bcr.bazel.build/modules/tar.bzl/0.7.0/MODULE.bazel": "cc1acd85da33c80e430b65219a620d54d114628df24a618c3a5fa0b65e988da9", + "https://bcr.bazel.build/modules/tar.bzl/0.7.0/source.json": "9becb80306f42d4810bfa16379fb48aad0b01ce5342bc12fe47dcd6af3ac4d7a", "https://bcr.bazel.build/modules/upb/0.0.0-20220923-a547704/MODULE.bazel": "7298990c00040a0e2f121f6c32544bab27d4452f80d9ce51349b1a28f3005c43", "https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072", "https://bcr.bazel.build/modules/yq.bzl/0.2.0/MODULE.bazel": "6f3a675677db8885be4d607fde14cc51829715e3a879fb016eb9bf336786ce6d", - "https://bcr.bazel.build/modules/yq.bzl/0.3.1/MODULE.bazel": "9bcb7151b3cd4681b89d350530eaf7b45e32a44dda94843b8932b0cb1cd4594a", - "https://bcr.bazel.build/modules/yq.bzl/0.3.1/source.json": "f0b0f204a2a6b0e34b4c9541efe8c04f2ef1af65948daa784eccea738b21dbd2", + "https://bcr.bazel.build/modules/yq.bzl/0.3.2/MODULE.bazel": "0384efa70e8033d842ea73aa4b7199fa099709e236a7264345c03937166670b6", + "https://bcr.bazel.build/modules/yq.bzl/0.3.2/source.json": "c4ec3e192477e154f08769e29d69e8fd36e8a4f0f623997f3e1f6f7d328f7d7d", "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", "https://bcr.bazel.build/modules/zlib/1.2.12/MODULE.bazel": "3b1a8834ada2a883674be8cbd36ede1b6ec481477ada359cd2d3ddc562340b27", "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", @@ -207,8 +210,8 @@ "moduleExtensions": { "@@aspect_rules_esbuild~//esbuild:extensions.bzl%esbuild": { "general": { - "bzlTransitiveDigest": "2t/OGeKfMCAl1xoAFVhaT+JKQb5zexk164MjT7t8SPE=", - "usagesDigest": "H070ZIHhSlR+Han009l+GdDSuT9AJssdyVHQ7xjstSo=", + "bzlTransitiveDigest": "Kp1ElwnSsU9URW4hnpM9wzhITxGUNAp4tu7dOwA82Qo=", + "usagesDigest": "w3kRc6iou9hC6M+vwECvdfk0P8nsVNjHSl4Ftru44zU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -218,6 +221,7 @@ "ruleClassName": "esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", + "integrity": "", "platform": "darwin-x64" } }, @@ -226,6 +230,7 @@ "ruleClassName": "esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", + "integrity": "", "platform": "darwin-arm64" } }, @@ -234,6 +239,7 @@ "ruleClassName": "esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", + "integrity": "", "platform": "linux-x64" } }, @@ -242,6 +248,7 @@ "ruleClassName": "esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", + "integrity": "", "platform": "linux-arm64" } }, @@ -250,6 +257,7 @@ "ruleClassName": "esbuild_repositories", "attributes": { "esbuild_version": "0.19.9", + "integrity": "", "platform": "win32-x64" } }, @@ -286,8 +294,7 @@ "extra_build_content": "", "generate_bzl_library_targets": false, "extract_full_archive": false, - "exclude_package_contents": [], - "system_tar": "auto" + "exclude_package_contents": [] } }, "npm__esbuild_0.19.9__links": { @@ -362,6 +369,11 @@ "aspect_tools_telemetry_report", "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" ], + [ + "aspect_rules_js~", + "bazel_lib", + "bazel_lib~" + ], [ "aspect_rules_js~", "bazel_skylib", @@ -372,10 +384,20 @@ "bazel_tools", "bazel_tools" ], + [ + "bazel_lib~", + "bazel_skylib", + "bazel_skylib~" + ], + [ + "bazel_lib~", + "bazel_tools", + "bazel_tools" + ], [ "tar.bzl~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "bazel_lib", + "bazel_lib~" ], [ "tar.bzl~", @@ -392,8 +414,8 @@ }, "@@aspect_rules_js~//npm:extensions.bzl%pnpm": { "general": { - "bzlTransitiveDigest": "SVyYFkMQbjQ0jUKo5pfA4RvHwE9U+087GVDEKYPcTSU=", - "usagesDigest": "IVicAE5kmPjEcCQ3BjtqveHxlxyM0WJ/OuayGov8SLE=", + "bzlTransitiveDigest": "b2732vQWZpf2g1U6Rf2M0kXVkyN5QVnEn69W2+zHZPQ=", + "usagesDigest": "aGI0J/oETkNLCXhrpWEDlZEAKB8hs563suxOHlpcTj0=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -403,11 +425,11 @@ "ruleClassName": "npm_import_rule", "attributes": { "package": "pnpm", - "version": "8.6.7", + "version": "8.15.9", "root_package": "", "link_workspace": "", "link_packages": {}, - "integrity": "sha512-vRIWpD/L4phf9Bk2o/O2TDR8fFoJnpYrp2TKqTIZF/qZ2/rgL3qKXzHofHgbXsinwMoSEigz28sqk3pQ+yMEQQ==", + "integrity": "sha512-SZQ0ydj90aJ5Tr9FUrOyXApjOrzuW7Fee13pDzL0e1E6ypjNXP0AHDHw20VLw4BO3M1XhQHkyik6aBYWa72fgQ==", "url": "", "commit": "", "patch_args": [ @@ -423,8 +445,7 @@ "extra_build_content": "load(\"@aspect_rules_js//js:defs.bzl\", \"js_binary\")\njs_binary(name = \"pnpm\", data = glob([\"package/**\"]), entry_point = \"package/dist/pnpm.cjs\", visibility = [\"//visibility:public\"])", "generate_bzl_library_targets": false, "extract_full_archive": true, - "exclude_package_contents": [], - "system_tar": "auto" + "exclude_package_contents": [] } }, "pnpm__links": { @@ -432,7 +453,7 @@ "ruleClassName": "npm_import_links", "attributes": { "package": "pnpm", - "version": "8.6.7", + "version": "8.15.9", "dev": false, "root_package": "", "link_packages": {}, @@ -489,6 +510,11 @@ "bazel_features", "bazel_features~" ], + [ + "aspect_rules_js~", + "bazel_lib", + "bazel_lib~" + ], [ "aspect_rules_js~", "bazel_skylib", @@ -509,10 +535,20 @@ "bazel_features_version", "bazel_features~~version_extension~bazel_features_version" ], + [ + "bazel_lib~", + "bazel_skylib", + "bazel_skylib~" + ], + [ + "bazel_lib~", + "bazel_tools", + "bazel_tools" + ], [ "tar.bzl~", - "aspect_bazel_lib", - "aspect_bazel_lib~" + "bazel_lib", + "bazel_lib~" ], [ "tar.bzl~", @@ -529,7 +565,7 @@ }, "@@aspect_rules_ts~//ts:extensions.bzl%ext": { "general": { - "bzlTransitiveDigest": "9IJp6IlB/FMHFBJe4MX/DQM4zi3oArC8yqYE/+NyPwk=", + "bzlTransitiveDigest": "pEu5+6q07zdUqscbVkhWTNLGT9OGRpnFCF4OGHv1n1k=", "usagesDigest": "PB65rYTG/QbLoSQfRhLDeERG+0m7bjTPzYXBqieQ5/4=", "recordedFileInputs": { "@@rules_browsers~//package.json": "84dc1ba9b1c667a25894e97218bd8f247d54f24bb694efb397a881be3c06a4c5" @@ -589,11 +625,6 @@ "aspect_rules_ts", "aspect_rules_ts~" ], - [ - "aspect_rules_ts~", - "aspect_tools_telemetry_report", - "aspect_tools_telemetry~~telemetry~aspect_tools_telemetry_report" - ], [ "aspect_rules_ts~", "bazel_tools", @@ -605,7 +636,7 @@ "@@aspect_tools_telemetry~//:extension.bzl%telemetry": { "general": { "bzlTransitiveDigest": "gA7tPEdJXhskzPIEUxjX9IdDrM6+WjfbgXJ8Ez47umk=", - "usagesDigest": "+Hur2pWe/TT3snEvJg4r10bQxD7lA5FHQPZQEHH32bY=", + "usagesDigest": "nyE85/XALFsAElY+d7H265tevR5W40tapQPfhmeIU2E=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -615,9 +646,9 @@ "ruleClassName": "tel_repository", "attributes": { "deps": { - "aspect_rules_js": "2.7.0", - "aspect_rules_ts": "3.7.0", - "aspect_rules_esbuild": "0.23.0", + "aspect_rules_js": "2.8.2", + "aspect_rules_ts": "3.7.1", + "aspect_rules_esbuild": "0.24.0", "aspect_tools_telemetry": "0.2.8" } } @@ -639,7 +670,7 @@ }, "@@pybind11_bazel~//:python_configure.bzl%extension": { "general": { - "bzlTransitiveDigest": "whINYge95GgPtysKDbNHQ0ZlWYdtKybHs5y2tLF+x7Q=", + "bzlTransitiveDigest": "dFd3A3f+jPCss+EDKMp/jxjcUhfMku130eT1KGxSCwA=", "usagesDigest": "gNvOHVcAlwgDsNXD0amkv2CC96mnaCThPQoE44y8K+w=", "recordedFileInputs": { "@@pybind11_bazel~//MODULE.bazel": "88af1c246226d87e65be78ed49ecd1e6f5e98648558c14ce99176da041dc378e" @@ -711,7 +742,7 @@ }, "@@rules_browsers~//browsers:extensions.bzl%browsers": { "general": { - "bzlTransitiveDigest": "6QMCx97Hwh2hyQPqZEA9AKAxbpygF41+K8xJfeqJYm8=", + "bzlTransitiveDigest": "ljZlVgWkQJnI6EvlHVfYit2EttUE52gDTbvmota5YO8=", "usagesDigest": "1PlExi+b77pSr2tAxFCVbpCtFoA7oixHabaL3dmas4Y=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -721,9 +752,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "4bc6d611d55dc96b213c8605cb8ac27d3c21973bf8b663df4cbf756c989e6745", + "sha256": "1419fa328bd7ea2697f26412ec693867516e4ef23c32eb13143a0b0b179b604b", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/linux64/chrome-headless-shell-linux64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/linux64/chrome-headless-shell-linux64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-linux64/chrome-headless-shell" @@ -740,9 +771,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "830cc2aafedbe7c9fe671c9898046f8900c06da89d12653ddc3ef26084d2f516", + "sha256": "792cbf9b77219b4476e41c49647bcd15e55f0988002fa1e4e6a720eb430c7eda", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/mac-x64/chrome-headless-shell-mac-x64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/mac-x64/chrome-headless-shell-mac-x64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-x64/chrome-headless-shell" @@ -759,9 +790,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "5b5792f5c2d05c3f1f782346910869b61a37b9003f212315b19f4e46710cf8b9", + "sha256": "f0c1917769775e826dfa69936381d0d95b06fe67cf631ecd842380d5de0e4c7f", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/mac-arm64/chrome-headless-shell-mac-arm64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/mac-arm64/chrome-headless-shell-mac-arm64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-mac-arm64/chrome-headless-shell" @@ -778,9 +809,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "19bdbf6e1579b6c056b74709520ac9df573f4e80e4f026cc2360a29443cf6c0c", + "sha256": "6ce0f20dd743a804890f45f5349370e1aa7cd3ac3482c04686fcff5fafd01bb3", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/win64/chrome-headless-shell-win64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/win64/chrome-headless-shell-win64.zip" ], "named_files": { "CHROME-HEADLESS-SHELL": "chrome-headless-shell-win64/chrome-headless-shell.exe" @@ -797,9 +828,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "ea41e7a217d878c00e9d66a0724ff54be7d02d08adb7f6458b7d8487b6fbcd84", + "sha256": "baf4bf9d22881265487732f17d35a49e9aadd0837aa5c1c1eea520c8aa24a97f", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/linux64/chromedriver-linux64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/linux64/chromedriver-linux64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-linux64/chromedriver" @@ -814,9 +845,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "aede9b67301b930ff9c673df28429aa82ce05c105a4ccbef7e0cd30a97ae429d", + "sha256": "87560768d5aa203b37c0a1b8459a35b05e4ece54afee2df530f3bc33de4f63c5", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/mac-x64/chromedriver-mac-x64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/mac-x64/chromedriver-mac-x64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-mac-x64/chromedriver" @@ -831,9 +862,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "5adf89a3e8edc6755920f4cfe2fe0515d40684878ef5201da5e02a9d491c4003", + "sha256": "99821795fa7c87eb92fb15248e23b237c83f397486d22ad9a10771622c36a5a0", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/mac-arm64/chromedriver-mac-arm64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/mac-arm64/chromedriver-mac-arm64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-mac-arm64/chromedriver" @@ -848,9 +879,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "a3dfe62b3e9e7a42bd324c07dcbbcc3a733a736b2a59f0e93b9250b88103ab73", + "sha256": "6e180e234a710c3cbf69566f64a662ed85473db6ae82275fd359f80ab288df99", "urls": [ - "https://storage.googleapis.com/chrome-for-testing-public/143.0.7482.0/win64/chromedriver-win64.zip" + "https://storage.googleapis.com/chrome-for-testing-public/144.0.7531.0/win64/chromedriver-win64.zip" ], "named_files": { "CHROMEDRIVER": "chromedriver-win64/chromedriver.exe" @@ -865,9 +896,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "c66a48222ff67d51560240d321895c6926c9b3af345cbf688ced8517781d88d1", + "sha256": "00fb922cda6bab971e02bcbfb77923b0a234388ed7d77c23506ca0a1a61d4a86", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/144.0/linux-x86_64/en-US/firefox-144.0.tar.xz" + "https://archive.mozilla.org/pub/firefox/releases/145.0/linux-x86_64/en-US/firefox-145.0.tar.xz" ], "named_files": { "FIREFOX": "firefox/firefox" @@ -882,9 +913,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "1e444b80921bc999d56c05a7decc1eaf88c0297cac5b90416299af2c77f5ecc9", + "sha256": "1c4556480deac8424049f3081a6de1e2c6de619bab3e8ce53e5a497b8d6d919e", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/144.0/mac/en-US/Firefox%20144.0.dmg" + "https://archive.mozilla.org/pub/firefox/releases/145.0/mac/en-US/Firefox%20145.0.dmg" ], "named_files": { "FIREFOX": "Firefox.app/Contents/MacOS/firefox" @@ -899,9 +930,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "1e444b80921bc999d56c05a7decc1eaf88c0297cac5b90416299af2c77f5ecc9", + "sha256": "1c4556480deac8424049f3081a6de1e2c6de619bab3e8ce53e5a497b8d6d919e", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/144.0/mac/en-US/Firefox%20144.0.dmg" + "https://archive.mozilla.org/pub/firefox/releases/145.0/mac/en-US/Firefox%20145.0.dmg" ], "named_files": { "FIREFOX": "Firefox.app/Contents/MacOS/firefox" @@ -916,9 +947,9 @@ "bzlFile": "@@rules_browsers~//browsers/private:browser_repo.bzl", "ruleClassName": "browser_repo", "attributes": { - "sha256": "d1e8a7c061e25a41c8dfa85e3aee8e86e9263c69104d80906c978c8d0556563a", + "sha256": "4b0345c113242653d923b369fcbd48e3089c57658f8c1542f887c8a375d50306", "urls": [ - "https://archive.mozilla.org/pub/firefox/releases/144.0/win64/en-US/Firefox%20Setup%20144.0.exe" + "https://archive.mozilla.org/pub/firefox/releases/145.0/win64/en-US/Firefox%20Setup%20145.0.exe" ], "named_files": { "FIREFOX": "core/firefox.exe" @@ -935,7 +966,7 @@ }, "@@rules_fuzzing~//fuzzing/private:extensions.bzl%non_module_dependencies": { "general": { - "bzlTransitiveDigest": "hVgJRQ3Er45/UUAgNn1Yp2Khcp/Y8WyafA2kXIYmQ5M=", + "bzlTransitiveDigest": "VMhyxXtdJvrNlLts7afAymA+pOatXuh5kLdxzVAZ/04=", "usagesDigest": "YnIrdgwnf3iCLfChsltBdZ7yOJh706lpa2vww/i2pDI=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1026,7 +1057,7 @@ }, "@@rules_java~//java:rules_java_deps.bzl%compatibility_proxy": { "general": { - "bzlTransitiveDigest": "KIX40nDfygEWbU+rq3nYpt3tVgTK/iO8PKh5VMBlN7M=", + "bzlTransitiveDigest": "C4xqrMy1wN4iuTN6Z2eCm94S5XingHhD6uwrIXvCxVI=", "usagesDigest": "pwHZ+26iLgQdwvdZeA5wnAjKnNI3y6XO2VbhOTeo5h8=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1049,7 +1080,7 @@ }, "@@rules_kotlin~//src/main/starlark/core/repositories:bzlmod_setup.bzl%rules_kotlin_extensions": { "general": { - "bzlTransitiveDigest": "fus14IFJ/1LGWWGKPH/U18VnJCoMjfDt1ckahqCnM0A=", + "bzlTransitiveDigest": "eecmTsmdIQveoA97hPtH3/Ej/kugbdCI24bhXIXaly8=", "usagesDigest": "aJF6fLy82rR95Ff5CZPAqxNoFgOMLMN5ImfBS0nhnkg=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, @@ -1118,8 +1149,8 @@ }, "@@rules_nodejs~//nodejs:extensions.bzl%node": { "general": { - "bzlTransitiveDigest": "71PwVsMlLx+RWdt1SI9nSqRHX7DX/NstWwr7/XBxEMs=", - "usagesDigest": "lqo/UXkPCwj19uB1o0D7KeWvm99ttcmhk7BOoYRXRp0=", + "bzlTransitiveDigest": "NwcLXHrbh2hoorA/Ybmcpjxsn/6avQmewDglodkDrgo=", + "usagesDigest": "R8gkuknFYSnypceao7XEOC2RaBVFY7V5K0xvOZHUZyY=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, @@ -1662,7 +1693,7 @@ }, "@@rules_python~//python/extensions:pip.bzl%pip": { "general": { - "bzlTransitiveDigest": "sQcIxpGBMBOMzUZK9ARAkAR7oUiEiGgKZhHLEf9Prfk=", + "bzlTransitiveDigest": "VKf3JZIRvp7gyc5Q9pSqri7bmB3079s5o6Yg7IaUHZI=", "usagesDigest": "K3E4RGDnEgGXkrLOS8/ma4NTUiLvGkMrRIhPiFxv8u0=", "recordedFileInputs": { "@@rules_python~//tools/publish/requirements_linux.txt": "8175b4c8df50ae2f22d1706961884beeb54e7da27bd2447018314a175981997d", @@ -4581,7 +4612,7 @@ "@@yq.bzl~//yq:extensions.bzl%yq": { "general": { "bzlTransitiveDigest": "61Uz+o5PnlY0jJfPZEUNqsKxnM/UCLeWsn5VVCc8u5Y=", - "usagesDigest": "X+3wxc/+KjF0tyJJ5qca2U/BgoeAEmAD0kuz8iBcX+0=", + "usagesDigest": "d69aUcr/oJv29ydTdFN0JWU618+QzKkxecQFV9S2xjU=", "recordedFileInputs": {}, "recordedDirentsInputs": {}, "envVariables": {}, diff --git a/constants.bzl b/constants.bzl index 04e088577dea..bc0ba7ab03a6 100644 --- a/constants.bzl +++ b/constants.bzl @@ -3,10 +3,10 @@ RELEASE_ENGINES_NODE = "^20.19.0 || ^22.12.0 || >=24.0.0" RELEASE_ENGINES_NPM = "^6.11.0 || ^7.5.6 || >=8.0.0" RELEASE_ENGINES_YARN = ">= 1.13.0" -NG_PACKAGR_VERSION = "^21.0.0-next.0" -ANGULAR_FW_VERSION = "^21.0.0-next.0" -ANGULAR_FW_PEER_DEP = "^21.0.0-next.0" -NG_PACKAGR_PEER_DEP = "^21.0.0-next.0" +NG_PACKAGR_VERSION = "^21.0.0" +ANGULAR_FW_VERSION = "^21.0.0" +ANGULAR_FW_PEER_DEP = "^21.0.0" +NG_PACKAGR_PEER_DEP = "^21.0.0" # Baseline widely-available date in `YYYY-MM-DD` format which defines Angular's # browser support. This date serves as the source of truth for the Angular CLI's diff --git a/docs/process/release.md b/docs/process/release.md index 1e060ac2169b..5490e48e8279 100644 --- a/docs/process/release.md +++ b/docs/process/release.md @@ -69,10 +69,15 @@ Releasing is performed using Angular's unified release tooling. Each week, two r Once FW releases the actual minor/major release (for example: `13.0.0` or `13.1.0`), update dependencies with the following: -1. Update [`constants.bzl`](../../constants.bzl) so `@angular/core` and `ng-packagr` are using the release version (drop `-next.0`). - -Merge the above change in a separate PR which lands _after_ FW releases (or else CI will fail) but _before_ the CLI -release PR. Releases are built before the PR is sent for review, so any changes after that point won't be included in the release. +1. Update [`constants.bzl`](../../constants.bzl) so `@angular/core` and `ng-packagr` are using the release version (drop `-rc.*`). +2. Update all `package.json` dependencies on framework packages to to the release version (drop `-rc.*`). + - Components packages release _after_ CLI, so those should be left as `-rc.*`. +3. `pnpm install` to update the `pnpm-lock.yaml` file. + +Create a PR with the above changes ([example](https://github.com/angular/angular-cli/pull/31872)) and merge it directly to the RC +branch. This PR must land _after_ FW releases (or else CI will fail) but _before_ the CLI release PR. Releases are built before +the PR is sent for review, so any changes after that point won't be included in the release and this cannot be combined with the +release PR. **AFTER a minor OR major CLI release:** diff --git a/goldens/public-api/angular/build/index.api.md b/goldens/public-api/angular/build/index.api.md index 632618f3e27f..a51449319e47 100644 --- a/goldens/public-api/angular/build/index.api.md +++ b/goldens/public-api/angular/build/index.api.md @@ -112,6 +112,9 @@ export enum BuildOutputFileType { export type DevServerBuilderOptions = { allowedHosts?: AllowedHosts; buildTarget: string; + define?: { + [key: string]: string; + }; headers?: { [key: string]: string; }; @@ -235,8 +238,10 @@ export type UnitTestBuilderOptions = { providersFile?: string; reporters?: SchemaReporter[]; runner?: Runner; + runnerConfig?: RunnerConfig; setupFiles?: string[]; tsConfig?: string; + ui?: boolean; watch?: boolean; }; diff --git a/goldens/public-api/angular_devkit/build_webpack/index.api.md b/goldens/public-api/angular_devkit/build_webpack/index.api.md index db2b47c9ed74..0d60187627d5 100644 --- a/goldens/public-api/angular_devkit/build_webpack/index.api.md +++ b/goldens/public-api/angular_devkit/build_webpack/index.api.md @@ -7,8 +7,8 @@ import { BuilderContext } from '@angular-devkit/architect'; import { BuilderOutput } from '@angular-devkit/architect'; import { Observable } from 'rxjs'; -import webpack from 'webpack'; -import WebpackDevServer from 'webpack-dev-server'; +import type webpack from 'webpack'; +import type WebpackDevServer from 'webpack-dev-server'; // @public (undocumented) export type BuildResult = BuilderOutput & { diff --git a/modules/testing/builder/BUILD.bazel b/modules/testing/builder/BUILD.bazel index 9b88a714cd05..7f542efb0138 100644 --- a/modules/testing/builder/BUILD.bazel +++ b/modules/testing/builder/BUILD.bazel @@ -20,6 +20,7 @@ ts_project( # Needed at runtime by some builder tests relying on SSR being # resolvable in the test project. ":node_modules/@angular/ssr", + ":node_modules/browser-sync", ":node_modules/jsdom", ":node_modules/vitest", ":node_modules/@vitest/coverage-v8", diff --git a/modules/testing/builder/package.json b/modules/testing/builder/package.json index ee6fa056bbfe..7d04585665af 100644 --- a/modules/testing/builder/package.json +++ b/modules/testing/builder/package.json @@ -4,9 +4,10 @@ "@angular-devkit/architect": "workspace:*", "@angular/ssr": "workspace:*", "@angular-devkit/build-angular": "workspace:*", - "@vitest/coverage-v8": "4.0.0", - "jsdom": "27.0.1", + "browser-sync": "3.0.4", + "@vitest/coverage-v8": "4.0.8", + "jsdom": "27.1.0", "rxjs": "7.8.2", - "vitest": "4.0.0" + "vitest": "4.0.8" } } diff --git a/modules/testing/builder/src/builder-harness.ts b/modules/testing/builder/src/builder-harness.ts index ec4973efa021..67b5f760d148 100644 --- a/modules/testing/builder/src/builder-harness.ts +++ b/modules/testing/builder/src/builder-harness.ts @@ -162,6 +162,24 @@ export class BuilderHarness { return this; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + modifyTarget( + targetName: string, + modifier: (options: O) => O | void, + ): this { + const target = this.builderTargets.get(targetName); + if (!target) { + throw new Error(`Target "${targetName}" not found.`); + } + + const newOptions = modifier(target.options as O); + if (newOptions) { + target.options = newOptions as json.JsonObject; + } + + return this; + } + execute( options: Partial = {}, ): Observable { diff --git a/package.json b/package.json index 85dbb509da7e..db622e668e4e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@angular/devkit-repo", - "version": "21.0.0-next.8", + "version": "21.0.1", "private": true, "description": "Software Development Kit for Angular", "keywords": [ @@ -15,6 +15,7 @@ "bazel": "bazelisk", "test": "bazel test //packages/...", "build": "pnpm -s admin build", + "build-schema": "bazel build //... --build_tag_filters schema --symlink_prefix dist-schema/", "lint": "eslint --cache --max-warnings=0 \"**/*.@(ts|mts|cts)\"", "templates": "pnpm -s admin templates", "validate": "pnpm -s admin validate", @@ -30,12 +31,12 @@ "type": "git", "url": "https://github.com/angular/angular-cli.git" }, - "packageManager": "pnpm@10.19.0", + "packageManager": "pnpm@10.23.0", "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "Please use pnpm instead of NPM to install dependencies", "yarn": "Please use pnpm instead of Yarn to install dependencies", - "pnpm": "10.19.0" + "pnpm": "10.23.0" }, "author": "Angular Authors", "license": "MIT", @@ -44,20 +45,21 @@ }, "homepage": "https://github.com/angular/angular-cli", "devDependencies": { - "@angular/animations": "21.0.0-next.9", - "@angular/cdk": "21.0.0-next.10", - "@angular/common": "21.0.0-next.9", - "@angular/compiler": "21.0.0-next.9", - "@angular/compiler-cli": "21.0.0-next.9", - "@angular/core": "21.0.0-next.9", - "@angular/forms": "21.0.0-next.9", - "@angular/localize": "21.0.0-next.9", - "@angular/material": "21.0.0-next.10", - "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#b69a61793bd6ba935af262297688408d0b48252e", - "@angular/platform-browser": "21.0.0-next.9", - "@angular/platform-server": "21.0.0-next.9", - "@angular/router": "21.0.0-next.9", - "@angular/service-worker": "21.0.0-next.9", + "@angular/animations": "21.0.1", + "@angular/cdk": "21.0.0", + "@angular/common": "21.0.1", + "@angular/compiler": "21.0.1", + "@angular/compiler-cli": "21.0.1", + "@angular/core": "21.0.1", + "@angular/forms": "21.0.1", + "@angular/localize": "21.0.1", + "@angular/material": "21.0.0", + "@angular/ng-dev": "https://github.com/angular/dev-infra-private-ng-dev-builds.git#4c28145df03aff8c74d0a53f4f5602140e4d1a23", + "@angular/platform-browser": "21.0.1", + "@angular/platform-server": "21.0.1", + "@angular/router": "21.0.1", + "@angular/service-worker": "21.0.1", + "@babel/core": "7.28.4", "@bazel/bazelisk": "1.26.0", "@bazel/buildifier": "8.2.1", "@eslint/compat": "1.4.0", @@ -96,8 +98,8 @@ "ajv": "8.17.1", "ansi-colors": "4.1.3", "buffer": "6.0.3", - "esbuild": "0.25.11", - "esbuild-wasm": "0.25.11", + "esbuild": "0.26.0", + "esbuild-wasm": "0.26.0", "eslint": "9.38.0", "eslint-config-prettier": "10.1.8", "eslint-plugin-header": "3.1.1", @@ -168,6 +170,6 @@ } }, "resolutions": { - "typescript": "5.9.3" + "undici-types": "^7.16.0" } } diff --git a/packages/angular/build/BUILD.bazel b/packages/angular/build/BUILD.bazel index 05f42e4dcd95..d8f89be1d5cd 100644 --- a/packages/angular/build/BUILD.bazel +++ b/packages/angular/build/BUILD.bazel @@ -105,6 +105,7 @@ ts_project( ":node_modules/sass", ":node_modules/source-map-support", ":node_modules/tinyglobby", + ":node_modules/undici", ":node_modules/vite", ":node_modules/vitest", ":node_modules/watchpack", diff --git a/packages/angular/build/package.json b/packages/angular/build/package.json index 26ddc2986faa..268b1a61445c 100644 --- a/packages/angular/build/package.json +++ b/packages/angular/build/package.json @@ -27,7 +27,7 @@ "@vitejs/plugin-basic-ssl": "2.1.0", "beasties": "0.3.5", "browserslist": "^4.26.0", - "esbuild": "0.25.11", + "esbuild": "0.26.0", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", @@ -37,31 +37,32 @@ "parse5-html-rewriting-stream": "8.0.0", "picomatch": "4.0.3", "piscina": "5.1.3", - "rolldown": "1.0.0-beta.44", + "rolldown": "1.0.0-beta.47", "sass": "1.93.2", "semver": "7.7.3", "source-map-support": "0.5.21", "tinyglobby": "0.2.15", - "vite": "7.1.11", + "undici": "7.16.0", + "vite": "7.2.2", "watchpack": "2.4.4" }, "optionalDependencies": { "lmdb": "3.4.3" }, "devDependencies": { - "@angular/ssr": "workspace:*", "@angular-devkit/core": "workspace:*", - "jsdom": "27.0.1", + "@angular/ssr": "workspace:*", + "jsdom": "27.1.0", "less": "4.4.2", - "ng-packagr": "21.0.0-next.4", + "ng-packagr": "21.0.0", "postcss": "8.5.6", "rxjs": "7.8.2", - "vitest": "4.0.0" + "vitest": "4.0.8" }, "peerDependencies": { - "@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP", "@angular/compiler": "0.0.0-ANGULAR-FW-PEER-DEP", "@angular/compiler-cli": "0.0.0-ANGULAR-FW-PEER-DEP", + "@angular/core": "0.0.0-ANGULAR-FW-PEER-DEP", "@angular/localize": "0.0.0-ANGULAR-FW-PEER-DEP", "@angular/platform-browser": "0.0.0-ANGULAR-FW-PEER-DEP", "@angular/platform-server": "0.0.0-ANGULAR-FW-PEER-DEP", @@ -74,7 +75,7 @@ "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", "tslib": "^2.3.0", "typescript": ">=5.9 <6.0", - "vitest": "^4.0.0" + "vitest": "^4.0.8" }, "peerDependenciesMeta": { "@angular/core": { diff --git a/packages/angular/build/src/builders/application/chunk-optimizer.ts b/packages/angular/build/src/builders/application/chunk-optimizer.ts index 0ba059df291f..e6827479b784 100644 --- a/packages/angular/build/src/builders/application/chunk-optimizer.ts +++ b/packages/angular/build/src/builders/application/chunk-optimizer.ts @@ -253,7 +253,6 @@ export async function optimizeChunks( const result = await bundle.generate({ minify: { mangle: false, compress: false }, - advancedChunks: { minSize: 8192 }, sourcemap, chunkFileNames: (chunkInfo) => `${chunkInfo.name.replace(/-[a-zA-Z0-9]{8}$/, '')}-[hash].js`, }); diff --git a/packages/angular/build/src/builders/application/options.ts b/packages/angular/build/src/builders/application/options.ts index 1b3a15b8cd56..25bd87253357 100644 --- a/packages/angular/build/src/builders/application/options.ts +++ b/packages/angular/build/src/builders/application/options.ts @@ -25,7 +25,7 @@ import { loadPostcssConfiguration, } from '../../utils/postcss-configuration'; import { getProjectRootPaths, normalizeDirectoryPath } from '../../utils/project-metadata'; -import { urlJoin } from '../../utils/url'; +import { addTrailingSlash, joinUrlParts } from '../../utils/url'; import { Schema as ApplicationBuilderOptions, ExperimentalPlatform, @@ -681,7 +681,9 @@ export function getLocaleBaseHref( const baseHrefSuffix = localeData.baseHref ?? localeData.subPath + '/'; - return baseHrefSuffix !== '' ? urlJoin(baseHref, baseHrefSuffix) : undefined; + return baseHrefSuffix !== '' + ? addTrailingSlash(joinUrlParts(baseHref, baseHrefSuffix)) + : undefined; } /** diff --git a/packages/angular/build/src/builders/application/schema.json b/packages/angular/build/src/builders/application/schema.json index c0a0f98313aa..8db4e6145b3f 100644 --- a/packages/angular/build/src/builders/application/schema.json +++ b/packages/angular/build/src/builders/application/schema.json @@ -611,7 +611,7 @@ }, "outputMode": { "type": "string", - "description": "Defines the build output target. 'static': Generates a static site for deployment on any static hosting service. 'server': Produces an application designed for deployment on a server that supports server-side rendering (SSR).", + "description": "Defines the type of build output artifact. 'static': Generates a static site build artifact for deployment on any static hosting service. 'server': Generates a server application build artifact, required for applications using hybrid rendering or APIs.", "enum": ["static", "server"] } }, diff --git a/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts b/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts index ecc460bcb405..ddae750a64a4 100644 --- a/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts +++ b/packages/angular/build/src/builders/application/tests/behavior/component-stylesheets_spec.ts @@ -11,7 +11,7 @@ import { APPLICATION_BUILDER_INFO, BASE_OPTIONS, describeBuilder } from '../setu describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { describe('Behavior: "Component Stylesheets"', () => { - it('should successfuly compile with an empty inline style', async () => { + it('should successfully compile with an empty inline style', async () => { await harness.modifyFile('src/app/app.component.ts', (content) => { return content.replace('styleUrls', 'styles').replace('./app.component.css', ''); }); diff --git a/packages/angular/build/src/builders/application/tests/options/external-dependencies_spec.ts b/packages/angular/build/src/builders/application/tests/options/external-dependencies_spec.ts index feb9b6447c3b..deb55e172109 100644 --- a/packages/angular/build/src/builders/application/tests/options/external-dependencies_spec.ts +++ b/packages/angular/build/src/builders/application/tests/options/external-dependencies_spec.ts @@ -24,7 +24,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { .content.not.toMatch(/from ['"]@angular\/common['"]/); }); - it('should only externalize the listed depedencies when option is set', async () => { + it('should only externalize the listed dependencies when option is set', async () => { harness.useTarget('build', { ...BASE_OPTIONS, externalDependencies: ['@angular/core'], @@ -39,7 +39,7 @@ describeBuilder(buildApplication, APPLICATION_BUILDER_INFO, (harness) => { .content.not.toMatch(/from ['"]@angular\/common['"]/); }); - it('should externalize the listed depedencies in Web Workers when option is set', async () => { + it('should externalize the listed dependencies in Web Workers when option is set', async () => { harness.useTarget('build', { ...BASE_OPTIONS, externalDependencies: ['path'], diff --git a/packages/angular/build/src/builders/dev-server/options.ts b/packages/angular/build/src/builders/dev-server/options.ts index 3e0f59319117..b6da278f2936 100644 --- a/packages/angular/build/src/builders/dev-server/options.ts +++ b/packages/angular/build/src/builders/dev-server/options.ts @@ -93,6 +93,7 @@ export async function normalizeOptions( poll, open, verbose, + define, watch, liveReload, hmr, @@ -114,6 +115,7 @@ export async function normalizeOptions( poll, open, verbose, + define, watch, liveReload: !!liveReload, hmr: hmr ?? !!liveReload, diff --git a/packages/angular/build/src/builders/dev-server/schema.json b/packages/angular/build/src/builders/dev-server/schema.json index 41902e43d8d0..023478ff7e52 100644 --- a/packages/angular/build/src/builders/dev-server/schema.json +++ b/packages/angular/build/src/builders/dev-server/schema.json @@ -53,6 +53,13 @@ } ] }, + "define": { + "description": "Defines global identifiers that will be replaced with a specified constant value when found in any JavaScript or TypeScript code including libraries. The value will be used directly. String values must be put in quotes. Identifiers within Angular metadata such as Component Decorators will not be replaced.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, "headers": { "type": "object", "description": "Custom HTTP headers to be added to all responses.", diff --git a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts index 65f34bddf94d..efdd749de258 100644 --- a/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts +++ b/packages/angular/build/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts @@ -105,8 +105,7 @@ DDIy4xXPW1STWfsmSYJfYW3wa0wk+pJQ3j2cTzkPQQ8gwpvM3U9DJl43uwb37v6I async function goToPageAndWaitForWS(page: Page, url: string): Promise { const baseUrl = url.replace(/^http/, 'ws'); - const socksRequest = - baseUrl[baseUrl.length - 1] === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; + const socksRequest = baseUrl.at(-1) === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; // Create a Chrome dev tools session so that we can capturing websocket request. // https://github.com/puppeteer/puppeteer/issues/2974 diff --git a/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts b/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts index ebbdcf3f1133..75a4e7935ebe 100644 --- a/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts +++ b/packages/angular/build/src/builders/dev-server/tests/execute-fetch.ts @@ -28,7 +28,7 @@ export async function executeOnceAndFetch( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); const originalResponse = await fetch(resolvedUrl, options?.request); response = originalResponse.clone(); @@ -68,7 +68,7 @@ export async function executeOnceAndGet( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); response = await new Promise((resolve) => diff --git a/packages/angular/build/src/builders/dev-server/tests/options/define_spec.ts b/packages/angular/build/src/builders/dev-server/tests/options/define_spec.ts new file mode 100644 index 000000000000..3c6ea08e15b4 --- /dev/null +++ b/packages/angular/build/src/builders/dev-server/tests/options/define_spec.ts @@ -0,0 +1,92 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { executeDevServer } from '../../index'; +import { executeOnceAndFetch } from '../execute-fetch'; +import { describeServeBuilder } from '../jasmine-helpers'; +import { BASE_OPTIONS, DEV_SERVER_BUILDER_INFO } from '../setup'; + +describeServeBuilder(executeDevServer, DEV_SERVER_BUILDER_INFO, (harness, setupTarget) => { + describe('option: "define"', () => { + beforeEach(() => { + setupTarget(harness); + + // Application code + harness.writeFile( + 'src/main.ts', + ` + // @ts-ignore + console.log(TEST); + // @ts-ignore + console.log(BUILD); + // @ts-ignore + console.log(SERVE); + `, + ); + }); + + it('should replace global identifiers in the application', async () => { + harness.useTarget('serve', { + ...BASE_OPTIONS, + define: { + TEST: JSON.stringify('test123'), + }, + }); + + const { result, response } = await executeOnceAndFetch(harness, '/main.js'); + + expect(result?.success).toBeTrue(); + const content = await response?.text(); + expect(content).toContain('console.log("test123")'); + }); + + it('should merge "define" option from dev-server and build', async () => { + harness.modifyTarget('build', (options) => { + options.define = { + BUILD: JSON.stringify('build'), + }; + }); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + define: { + SERVE: JSON.stringify('serve'), + }, + }); + + const { result, response } = await executeOnceAndFetch(harness, '/main.js'); + + expect(result?.success).toBeTrue(); + const content = await response?.text(); + expect(content).toContain('console.log("build")'); + expect(content).toContain('console.log("serve")'); + }); + + it('should overwrite "define" option from build with the one from dev-server', async () => { + harness.modifyTarget('build', (options) => { + options.define = { + TEST: JSON.stringify('build'), + }; + }); + + harness.useTarget('serve', { + ...BASE_OPTIONS, + define: { + TEST: JSON.stringify('serve'), + }, + }); + + const { result, response } = await executeOnceAndFetch(harness, '/main.js'); + + expect(result?.success).toBeTrue(); + const content = await response?.text(); + expect(content).toContain('console.log("serve")'); + expect(content).not.toContain('console.log("build")'); + }); + }); +}); diff --git a/packages/angular/build/src/builders/dev-server/vite/hmr.ts b/packages/angular/build/src/builders/dev-server/vite/hmr.ts index ae2fb1b91bb0..467962572463 100644 --- a/packages/angular/build/src/builders/dev-server/vite/hmr.ts +++ b/packages/angular/build/src/builders/dev-server/vite/hmr.ts @@ -57,9 +57,12 @@ export async function invalidateUpdatedFiles( } if (serverApplicationChanged) { - // Clear the server app cache and - // trigger module evaluation before reload to initiate dependency optimization. - const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')) as { + // Clear the server app cache and trigger module evaluation before reload to initiate dependency optimization. + // The querystring is needed as a workaround for: + // `ɵgetOrCreateAngularServerApp` can be undefined right after an error. + const { ɵdestroyAngularServerApp } = (await server.ssrLoadModule( + `/main.server.mjs?timestamp=${Date.now()}`, + )) as { ɵdestroyAngularServerApp: typeof destroyAngularServerApp; }; diff --git a/packages/angular/build/src/builders/dev-server/vite/index.ts b/packages/angular/build/src/builders/dev-server/vite/index.ts index b3a3ca985641..8129daac1ba1 100644 --- a/packages/angular/build/src/builders/dev-server/vite/index.ts +++ b/packages/angular/build/src/builders/dev-server/vite/index.ts @@ -14,7 +14,7 @@ import { join } from 'node:path'; import type { Connect, ViteDevServer } from 'vite'; import type { ComponentStyleRecord } from '../../../tools/vite/middlewares'; import { ServerSsrMode } from '../../../tools/vite/plugins'; -import { EsbuildLoaderOption } from '../../../tools/vite/utils'; +import { EsbuildLoaderOption, updateExternalMetadata } from '../../../tools/vite/utils'; import { normalizeSourceMaps } from '../../../utils'; import { useComponentStyleHmr, useComponentTemplateHmr } from '../../../utils/environment-options'; import { Result, ResultKind } from '../../application/results'; @@ -35,7 +35,6 @@ import { DevServerExternalResultMetadata, OutputAssetRecord, OutputFileRecord, - isAbsoluteUrl, updateResultRecord, } from './utils'; @@ -49,7 +48,7 @@ export type BuilderAction = ( * Build options that are also present on the dev server but are only passed * to the build. */ -const CONVENIENCE_BUILD_OPTIONS = ['watch', 'poll', 'verbose'] as const; +const CONVENIENCE_BUILD_OPTIONS = ['watch', 'poll', 'verbose', 'define'] as const; // eslint-disable-next-line max-lines-per-function export async function* serveWithVite( @@ -75,7 +74,15 @@ export async function* serveWithVite( for (const optionName of CONVENIENCE_BUILD_OPTIONS) { const optionValue = serverOptions[optionName]; if (optionValue !== undefined) { - rawBrowserOptions[optionName] = optionValue; + if (optionName === 'define' && rawBrowserOptions[optionName]) { + // Define has merging behavior within the application + for (const [key, value] of Object.entries(optionValue)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (rawBrowserOptions[optionName] as any)[key] = value; + } + } else { + rawBrowserOptions[optionName] = optionValue; + } } } @@ -236,9 +243,7 @@ export async function* serveWithVite( const baseHref = result.detail['htmlBaseHref'] as string; // Remove trailing slash serverOptions.servePath = - baseHref !== './' && baseHref[baseHref.length - 1] === '/' - ? baseHref.slice(0, -1) - : baseHref; + baseHref !== './' && baseHref.at(-1) === '/' ? baseHref.slice(0, -1) : baseHref; } assetFiles.clear(); @@ -325,34 +330,7 @@ export async function* serveWithVite( } // To avoid disconnecting the array objects from the option, these arrays need to be mutated instead of replaced. - if (result.detail?.['externalMetadata']) { - const { implicitBrowser, implicitServer, explicit } = result.detail[ - 'externalMetadata' - ] as ExternalResultMetadata; - const implicitServerFiltered = implicitServer.filter( - (m) => !isBuiltin(m) && !isAbsoluteUrl(m), - ); - const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m)); - - // Empty Arrays to avoid growing unlimited with every re-build. - externalMetadata.explicitBrowser.length = 0; - externalMetadata.explicitServer.length = 0; - externalMetadata.implicitServer.length = 0; - externalMetadata.implicitBrowser.length = 0; - - const externalDeps = browserOptions.externalDependencies ?? []; - externalMetadata.explicitBrowser.push(...explicit, ...externalDeps); - externalMetadata.explicitServer.push(...explicit, ...externalDeps, ...builtinModules); - externalMetadata.implicitServer.push(...implicitServerFiltered); - externalMetadata.implicitBrowser.push(...implicitBrowserFiltered); - - // The below needs to be sorted as Vite uses these options are part of the hashing invalidation algorithm. - // See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239 - externalMetadata.explicitBrowser.sort(); - externalMetadata.explicitServer.sort(); - externalMetadata.implicitServer.sort(); - externalMetadata.implicitBrowser.sort(); - } + updateExternalMetadata(result, externalMetadata, browserOptions.externalDependencies); if (server) { // Update fs allow list to include any new assets from the build option. diff --git a/packages/angular/build/src/builders/dev-server/vite/server.ts b/packages/angular/build/src/builders/dev-server/vite/server.ts index f527e41d33d0..73f58ad5c348 100644 --- a/packages/angular/build/src/builders/dev-server/vite/server.ts +++ b/packages/angular/build/src/builders/dev-server/vite/server.ts @@ -13,6 +13,7 @@ import type { ComponentStyleRecord } from '../../../tools/vite/middlewares'; import { ServerSsrMode, createAngularMemoryPlugin, + createAngularServerSideSSLPlugin, createAngularSetupMiddlewaresPlugin, createAngularSsrTransformPlugin, createRemoveIdPrefixPlugin, @@ -59,13 +60,7 @@ async function createServerConfig( headers: serverOptions.headers, // Disable the websocket if live reload is disabled (false/undefined are the only valid values) ws: serverOptions.liveReload === false && serverOptions.hmr === false ? false : undefined, - // When server-side rendering (SSR) is enabled togather with SSL and Express is being used, - // we must configure Vite to use HTTP/1.1. - // This is necessary because Express does not support HTTP/2. - // We achieve this by defining an empty proxy. - // See: https://github.com/vitejs/vite/blob/c4b532cc900bf988073583511f57bd581755d5e3/packages/vite/src/node/http.ts#L106 - proxy: - serverOptions.ssl && ssrMode === ServerSsrMode.ExternalSsrMiddleware ? (proxy ?? {}) : proxy, + proxy, cors: { // This will add the header `Access-Control-Allow-Origin: http://example.com`, // where `http://example.com` is the requesting origin. @@ -207,16 +202,19 @@ export async function setupServer( preTransformRequests, cacheDir, ), - ssr: createSsrConfig( - externalMetadata, - serverOptions, - prebundleTransformer, - zoneless, - target, - prebundleLoaderExtensions, - thirdPartySourcemaps, - define, - ), + ssr: + ssrMode === ServerSsrMode.NoSsr + ? undefined + : createSsrConfig( + externalMetadata, + serverOptions, + prebundleTransformer, + zoneless, + target, + prebundleLoaderExtensions, + thirdPartySourcemaps, + define, + ), plugins: [ createAngularSetupMiddlewaresPlugin({ outputFiles, @@ -258,11 +256,15 @@ export async function setupServer( }; if (serverOptions.ssl) { + configuration.plugins ??= []; if (!serverOptions.sslCert || !serverOptions.sslKey) { const { default: basicSslPlugin } = await import('@vitejs/plugin-basic-ssl'); - configuration.plugins ??= []; configuration.plugins.push(basicSslPlugin()); } + + if (ssrMode !== ServerSsrMode.NoSsr) { + configuration.plugins?.push(createAngularServerSideSSLPlugin()); + } } return configuration; diff --git a/packages/angular/build/src/builders/karma/find-tests.ts b/packages/angular/build/src/builders/karma/find-tests.ts index 62bcd563d455..00468146df5f 100644 --- a/packages/angular/build/src/builders/karma/find-tests.ts +++ b/packages/angular/build/src/builders/karma/find-tests.ts @@ -6,6 +6,27 @@ * found in the LICENSE file at https://angular.dev/license */ +import { findTests as findTestsBase } from '../unit-test/test-discovery'; + // This file is a compatibility layer that re-exports the test discovery logic from its new location. // This is necessary to avoid breaking the Karma builder, which still depends on this file. -export { findTests, getTestEntrypoints } from '../unit-test/test-discovery'; +export { getTestEntrypoints } from '../unit-test/test-discovery'; + +const removeLeadingSlash = (path: string): string => { + return path.startsWith('/') ? path.substring(1) : path; +}; + +export async function findTests( + include: string[], + exclude: string[], + workspaceRoot: string, + projectSourceRoot: string, +): Promise { + // Karma has legacy support for workspace "root-relative" file paths + return findTestsBase( + include.map(removeLeadingSlash), + exclude.map(removeLeadingSlash), + workspaceRoot, + projectSourceRoot, + ); +} diff --git a/packages/angular/build/src/builders/karma/progress-reporter.ts b/packages/angular/build/src/builders/karma/progress-reporter.ts index c4f573e9a2b2..908f1c856e6d 100644 --- a/packages/angular/build/src/builders/karma/progress-reporter.ts +++ b/packages/angular/build/src/builders/karma/progress-reporter.ts @@ -40,6 +40,8 @@ export function injectKarmaReporter( class ProgressNotifierReporter { static $inject = ['emitter', LATEST_BUILD_FILES_TOKEN]; + // Needed for the karma reporter interface, see https://github.com/angular/angular-cli/issues/31629 + adapters = []; constructor( private readonly emitter: KarmaEmitter, diff --git a/packages/angular/build/src/builders/unit-test/builder.ts b/packages/angular/build/src/builders/unit-test/builder.ts index 4785c6c6d16c..122039595bd9 100644 --- a/packages/angular/build/src/builders/unit-test/builder.ts +++ b/packages/angular/build/src/builders/unit-test/builder.ts @@ -146,6 +146,13 @@ async function* runBuildAndTest( } catch (e) { assertIsError(e); context.logger.error(`An exception occurred during test execution:\n${e.stack ?? e.message}`); + if (e instanceof AggregateError) { + e.errors.forEach((inner) => { + assertIsError(inner); + context.logger.error(inner.stack ?? inner.message); + }); + } + yield { success: false }; consecutiveErrorCount++; } @@ -176,10 +183,6 @@ export async function* execute( return; } - context.logger.warn( - `NOTE: The "unit-test" builder is currently EXPERIMENTAL and not ready for production use.`, - ); - // Initialize the test runner and normalize options let runner; let normalizedOptions; diff --git a/packages/angular/build/src/builders/unit-test/options.ts b/packages/angular/build/src/builders/unit-test/options.ts index 95e4afb5305c..8fa5b14eda59 100644 --- a/packages/angular/build/src/builders/unit-test/options.ts +++ b/packages/angular/build/src/builders/unit-test/options.ts @@ -12,7 +12,7 @@ import path from 'node:path'; import { normalizeCacheOptions } from '../../utils/normalize-cache'; import { getProjectRootPaths } from '../../utils/project-metadata'; import { isTTY } from '../../utils/tty'; -import type { Schema as UnitTestBuilderOptions } from './schema'; +import { Runner, type Schema as UnitTestBuilderOptions } from './schema'; export type NormalizedUnitTestBuilderOptions = Awaited>; @@ -54,7 +54,12 @@ export async function normalizeOptions( const buildTargetSpecifier = options.buildTarget ?? `::development`; const buildTarget = targetFromTargetString(buildTargetSpecifier, projectName, 'build'); - const { runner, browsers, progress, filter, browserViewport } = options; + const { runner, browsers, progress, filter, browserViewport, ui, runnerConfig } = options; + + if (ui && runner !== Runner.Vitest) { + throw new Error('The "ui" option is only available for the "vitest" runner.'); + } + const [width, height] = browserViewport?.split('x').map(Number) ?? []; let tsConfig = options.tsConfig; @@ -71,6 +76,14 @@ export async function normalizeOptions( } } + let watch = options.watch ?? isTTY(); + if (options.ui && options.watch === false) { + context.logger.warn( + `The '--ui' option requires watch mode. The '--no-watch' flag will be ignored.`, + ); + watch = true; + } + return { // Project/workspace information workspaceRoot, @@ -82,37 +95,43 @@ export async function normalizeOptions( include: options.include ?? ['**/*.spec.ts'], exclude: options.exclude, filter, - runnerName: runner ?? 'vitest', - coverage: options.coverage - ? { - exclude: options.coverageExclude, - include: options.coverageInclude, - reporters: normalizeReporterOption(options.coverageReporters), - thresholds: options.coverageThresholds, - // The schema generation tool doesn't support tuple types for items, but the schema validation - // does ensure that the array has exactly two numbers. - watermarks: options.coverageWatermarks as { - statements?: [number, number]; - branches?: [number, number]; - functions?: [number, number]; - lines?: [number, number]; - }, - } - : undefined, + runnerName: runner ?? Runner.Vitest, + coverage: { + enabled: options.coverage, + exclude: options.coverageExclude, + include: options.coverageInclude, + reporters: normalizeReporterOption(options.coverageReporters), + thresholds: options.coverageThresholds, + // The schema generation tool doesn't support tuple types for items, but the schema validation + // does ensure that the array has exactly two numbers. + watermarks: options.coverageWatermarks as { + statements?: [number, number]; + branches?: [number, number]; + functions?: [number, number]; + lines?: [number, number]; + }, + }, tsConfig, buildProgress: progress, reporters: normalizeReporterOption(options.reporters), outputFile: options.outputFile, browsers, browserViewport: width && height ? { width, height } : undefined, - watch: options.watch ?? isTTY(), + watch, debug: options.debug ?? false, + ui: options.ui ?? false, providersFile: options.providersFile && path.join(workspaceRoot, options.providersFile), setupFiles: options.setupFiles ? options.setupFiles.map((setupFile) => path.join(workspaceRoot, setupFile)) : [], dumpVirtualFiles: options.dumpVirtualFiles, listTests: options.listTests, + runnerConfig: + typeof runnerConfig === 'string' + ? runnerConfig.length === 0 + ? true + : path.resolve(workspaceRoot, runnerConfig) + : runnerConfig, }; } diff --git a/packages/angular/build/src/builders/unit-test/runners/karma/executor.ts b/packages/angular/build/src/builders/unit-test/runners/karma/executor.ts index c81f22d0b7d1..4b30ff0405bf 100644 --- a/packages/angular/build/src/builders/unit-test/runners/karma/executor.ts +++ b/packages/angular/build/src/builders/unit-test/runners/karma/executor.ts @@ -7,6 +7,8 @@ */ import type { BuilderContext, BuilderOutput } from '@angular-devkit/architect'; +import fs from 'node:fs/promises'; +import path from 'node:path'; import type { ApplicationBuilderInternalOptions } from '../../../application/options'; import type { KarmaBuilderOptions, KarmaBuilderTransformsOptions } from '../../../karma'; import { NormalizedUnitTestBuilderOptions } from '../../options'; @@ -50,7 +52,23 @@ export class KarmaExecutor implements TestExecutor { await context.getBuilderNameForTarget(unitTestOptions.buildTarget), )) as unknown as ApplicationBuilderInternalOptions; + let karmaConfig: string | undefined; + if (typeof unitTestOptions.runnerConfig === 'string') { + karmaConfig = unitTestOptions.runnerConfig; + context.logger.info(`Using Karma configuration file: ${karmaConfig}`); + } else if (unitTestOptions.runnerConfig) { + const potentialPath = path.join(unitTestOptions.projectRoot, 'karma.conf.js'); + try { + await fs.access(potentialPath); + karmaConfig = potentialPath; + context.logger.info(`Using Karma configuration file: ${karmaConfig}`); + } catch { + context.logger.info('No Karma configuration file found. Using default configuration.'); + } + } + const karmaOptions: KarmaBuilderOptions = { + karmaConfig, tsConfig: unitTestOptions.tsConfig ?? buildTargetOptions.tsConfig, polyfills: buildTargetOptions.polyfills, assets: buildTargetOptions.assets, @@ -69,8 +87,8 @@ export class KarmaExecutor implements TestExecutor { poll: buildTargetOptions.poll, preserveSymlinks: buildTargetOptions.preserveSymlinks, browsers: unitTestOptions.browsers?.join(','), - codeCoverage: !!unitTestOptions.coverage, - codeCoverageExclude: unitTestOptions.coverage?.exclude, + codeCoverage: unitTestOptions.coverage.enabled, + codeCoverageExclude: unitTestOptions.coverage.exclude, fileReplacements: buildTargetOptions.fileReplacements, reporters: unitTestOptions.reporters?.map((reporter) => { // Karma only supports string reporters. @@ -105,7 +123,7 @@ export class KarmaExecutor implements TestExecutor { } // Add coverage options - if (unitTestOptions.coverage) { + if (unitTestOptions.coverage.enabled) { const { thresholds, watermarks } = unitTestOptions.coverage; // eslint-disable-next-line @typescript-eslint/no-explicit-any const coverageReporter = ((options as any).coverageReporter ??= {}); diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts index 23ed9b9a23e8..3aeeb7afe474 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider.ts @@ -18,12 +18,16 @@ export interface BrowserConfiguration { function findBrowserProvider( projectResolver: NodeJS.RequireResolve, ): BrowserBuiltinProvider | undefined { + const requiresPreview = !!process.versions.webcontainer; + // One of these must be installed in the project to use browser testing - const vitestBuiltinProviders = ['playwright', 'webdriverio'] as const; + const vitestBuiltinProviders = requiresPreview + ? (['preview'] as const) + : (['playwright', 'webdriverio', 'preview'] as const); for (const providerName of vitestBuiltinProviders) { try { - projectResolver(providerName); + projectResolver(`@vitest/browser-${providerName}`); return providerName; } catch {} @@ -32,13 +36,17 @@ function findBrowserProvider( return undefined; } -function normalizeBrowserName(browserName: string): string { +function normalizeBrowserName(browserName: string): { browser: string; headless: boolean } { // Normalize browser names to match Vitest's expectations for headless but also supports karma's names // e.g., 'ChromeHeadless' -> 'chrome', 'FirefoxHeadless' -> 'firefox' // and 'Chrome' -> 'chrome', 'Firefox' -> 'firefox'. const normalized = browserName.toLowerCase(); + const headless = normalized.endsWith('headless'); - return normalized.replace(/headless$/, ''); + return { + browser: headless ? normalized.slice(0, -8) : normalized, + headless: headless, + }; } export async function setupBrowserConfiguration( @@ -67,12 +75,26 @@ export async function setupBrowserConfiguration( if (providerName) { const providerPackage = `@vitest/browser-${providerName}`; try { - const providerModule = await import(providerPackage); + const providerModule = await import(projectResolver(providerPackage)); // Validate that the imported module has the expected structure const providerFactory = providerModule[providerName]; if (typeof providerFactory === 'function') { - provider = providerFactory(); + if ( + providerName === 'playwright' && + process.env['CHROME_BIN']?.includes('rules_browsers') + ) { + // Use the Chrome binary from the 'rules_browsers' toolchain (via CHROME_BIN) + // for Playwright when available to ensure hermetic testing, preventing reliance + // on locally installed or NPM-managed browser versions. + provider = providerFactory({ + launchOptions: { + executablePath: process.env.CHROME_BIN, + }, + }); + } else { + provider = providerFactory(); + } } else { errors ??= []; errors.push( @@ -102,17 +124,23 @@ export async function setupBrowserConfiguration( } const isCI = !!process.env['CI']; - const headless = isCI || browsers.some((name) => name.toLowerCase().includes('headless')); + const instances = browsers.map(normalizeBrowserName); + if (providerName === 'preview') { + instances.forEach((instance) => { + instance.headless = false; + }); + } else if (isCI) { + instances.forEach((instance) => { + instance.headless = true; + }); + } const browser = { enabled: true, provider, - headless, - ui: !headless, + ui: !isCI && instances.some((instance) => !instance.headless), viewport, - instances: browsers.map((browserName) => ({ - browser: normalizeBrowserName(browserName), - })), + instances, } satisfies BrowserConfigOptions; return { browser }; diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts new file mode 100644 index 000000000000..2162ffa6ed5e --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/browser-provider_spec.ts @@ -0,0 +1,163 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { setupBrowserConfiguration } from './browser-provider'; + +describe('setupBrowserConfiguration', () => { + let workspaceRoot: string; + + beforeEach(async () => { + // Create a temporary workspace root + workspaceRoot = await mkdtemp(join(tmpdir(), 'angular-cli-test-')); + await writeFile(join(workspaceRoot, 'package.json'), '{}'); + + // Create a mock @vitest/browser-playwright package + const playwrightPkgPath = join(workspaceRoot, 'node_modules/@vitest/browser-playwright'); + await mkdir(playwrightPkgPath, { recursive: true }); + await writeFile( + join(playwrightPkgPath, 'package.json'), + JSON.stringify({ name: '@vitest/browser-playwright', main: 'index.js' }), + ); + await writeFile( + join(playwrightPkgPath, 'index.js'), + 'module.exports = { playwright: () => ({ name: "playwright" }) };', + ); + }); + + afterEach(async () => { + await rm(workspaceRoot, { recursive: true, force: true }); + }); + + it('should configure headless mode for specific browsers based on name', async () => { + const { browser } = await setupBrowserConfiguration( + ['ChromeHeadless', 'Firefox'], + false, + workspaceRoot, + undefined, + ); + + expect(browser?.enabled).toBeTrue(); + expect(browser?.instances).toEqual([ + { browser: 'chrome', headless: true }, + { browser: 'firefox', headless: false }, + ]); + }); + + it('should force headless mode in CI environment', async () => { + const originalCI = process.env['CI']; + process.env['CI'] = 'true'; + + try { + const { browser } = await setupBrowserConfiguration( + ['Chrome', 'FirefoxHeadless'], + false, + workspaceRoot, + undefined, + ); + + expect(browser?.instances).toEqual([ + { browser: 'chrome', headless: true }, + { browser: 'firefox', headless: true }, + ]); + } finally { + if (originalCI === undefined) { + delete process.env['CI']; + } else { + process.env['CI'] = originalCI; + } + } + }); + + it('should set ui property based on headless instances (local)', async () => { + // Local run (not CI) + const originalCI = process.env['CI']; + delete process.env['CI']; + + try { + // Case 1: All headless -> UI false + let result = await setupBrowserConfiguration( + ['ChromeHeadless'], + false, + workspaceRoot, + undefined, + ); + expect(result.browser?.ui).toBeFalse(); + + // Case 2: Mixed -> UI true + result = await setupBrowserConfiguration( + ['ChromeHeadless', 'Firefox'], + false, + workspaceRoot, + undefined, + ); + expect(result.browser?.ui).toBeTrue(); + } finally { + if (originalCI !== undefined) { + process.env['CI'] = originalCI; + } + } + }); + + it('should disable UI in CI even if headed browsers are requested', async () => { + const originalCI = process.env['CI']; + process.env['CI'] = 'true'; + + try { + const { browser } = await setupBrowserConfiguration( + ['Chrome'], + false, + workspaceRoot, + undefined, + ); + + expect(browser?.ui).toBeFalse(); + // And verify instances are forced to headless + expect(browser?.instances?.[0].headless).toBeTrue(); + } finally { + if (originalCI === undefined) { + delete process.env['CI']; + } else { + process.env['CI'] = originalCI; + } + } + }); + + it('should support Preview provider forcing headless false', async () => { + // Create mock preview package + const previewPkgPath = join(workspaceRoot, 'node_modules/@vitest/browser-preview'); + await mkdir(previewPkgPath, { recursive: true }); + await writeFile( + join(previewPkgPath, 'package.json'), + JSON.stringify({ name: '@vitest/browser-preview', main: 'index.js' }), + ); + await writeFile( + join(previewPkgPath, 'index.js'), + 'module.exports = { preview: () => ({ name: "preview" }) };', + ); + + // Remove playwright mock for this test to force usage of preview + await rm(join(workspaceRoot, 'node_modules/@vitest/browser-playwright'), { + recursive: true, + force: true, + }); + + const { browser } = await setupBrowserConfiguration( + ['ChromeHeadless'], + false, + workspaceRoot, + undefined, + ); + + expect(browser?.provider).toBeDefined(); + // Preview forces headless false + expect(browser?.instances?.[0].headless).toBeFalse(); + }); +}); diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts index 99ad334c1708..1c65d6fc4d50 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/build-options.ts @@ -33,18 +33,33 @@ function createTestBedInitVirtualFile( import { NgModule${usesZoneJS ? ', provideZoneChangeDetection' : ''} } from '@angular/core'; import { getTestBed, ɵgetCleanupHook as getCleanupHook } from '@angular/core/testing'; import { BrowserTestingModule, platformBrowserTesting } from '@angular/platform-browser/testing'; + import { afterEach, beforeEach } from 'vitest'; ${providersImport} + + // The beforeEach and afterEach hooks are registered outside the globalThis guard. + // This ensures that the hooks are always applied, even in non-isolated browser environments. // Same as https://github.com/angular/angular/blob/05a03d3f975771bb59c7eefd37c01fa127ee2229/packages/core/testing/srcs/test_hooks.ts#L21-L29 beforeEach(getCleanupHook(false)); afterEach(getCleanupHook(true)); - @NgModule({ - providers: [${usesZoneJS ? 'provideZoneChangeDetection(), ' : ''}...providers], - }) - export class TestModule {} - getTestBed().initTestEnvironment([BrowserTestingModule, TestModule], platformBrowserTesting(), { - errorOnUnknownElements: true, - errorOnUnknownProperties: true, - }); + + const ANGULAR_TESTBED_SETUP = Symbol.for('@angular/cli/testbed-setup'); + if (!globalThis[ANGULAR_TESTBED_SETUP]) { + globalThis[ANGULAR_TESTBED_SETUP] = true; + + // The Angular TestBed needs to be initialized before any tests are run. + // In a non-isolated environment, this setup file can be executed multiple times. + // The guard condition above ensures that the setup is only performed once. + + @NgModule({ + providers: [${usesZoneJS ? 'provideZoneChangeDetection(), ' : ''}...providers], + }) + class TestModule {} + + getTestBed().initTestEnvironment([BrowserTestingModule, TestModule], platformBrowserTesting(), { + errorOnUnknownElements: true, + errorOnUnknownProperties: true, + }); + } `; } @@ -83,6 +98,12 @@ export async function getVitestBuildOptions( }); entryPoints.set('init-testbed', 'angular:test-bed-init'); + // The 'vitest' package is always external for testing purposes + const externalDependencies = ['vitest']; + if (baseBuildOptions.externalDependencies) { + externalDependencies.push(...baseBuildOptions.externalDependencies); + } + const buildOptions: Partial = { ...baseBuildOptions, watch, @@ -101,7 +122,10 @@ export async function getVitestBuildOptions( outputHashing: adjustOutputHashing(baseBuildOptions.outputHashing), optimization: false, entryPoints, - externalDependencies: ['vitest', '@vitest/browser/context'], + // Enable support for vitest browser prebundling. Excludes can be controlled with a runnerConfig + // and the `optimizeDeps.exclude` option. + externalPackages: true, + externalDependencies, }; buildOptions.polyfills = injectTestingPolyfills(buildOptions.polyfills); diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/configuration.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/configuration.ts new file mode 100644 index 000000000000..6df583350e07 --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/configuration.ts @@ -0,0 +1,55 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +/** + * @fileoverview + * This file contains utility functions for finding the Vitest base configuration file. + */ + +import { readdir } from 'node:fs/promises'; +import path from 'node:path'; + +/** + * A list of potential Vitest configuration filenames. + * The order of the files is important as the first one found will be used. + */ +const POTENTIAL_CONFIGS = [ + 'vitest-base.config.ts', + 'vitest-base.config.mts', + 'vitest-base.config.cts', + 'vitest-base.config.js', + 'vitest-base.config.mjs', + 'vitest-base.config.cjs', +]; + +/** + * Finds the Vitest configuration file in the given search directories. + * + * @param searchDirs An array of directories to search for the configuration file. + * @returns The path to the configuration file, or `false` if no file is found. + * Returning `false` is used to disable Vitest's default configuration file search. + */ +export async function findVitestBaseConfig(searchDirs: string[]): Promise { + const uniqueDirs = new Set(searchDirs); + for (const dir of uniqueDirs) { + try { + const entries = await readdir(dir, { withFileTypes: true }); + const files = new Set(entries.filter((e) => e.isFile()).map((e) => e.name)); + + for (const potential of POTENTIAL_CONFIGS) { + if (files.has(potential)) { + return path.join(dir, potential); + } + } + } catch { + // Ignore directories that cannot be read + } + } + + return false; +} diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts index e665c54290df..bac8e389c4c5 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/executor.ts @@ -8,10 +8,13 @@ import type { BuilderOutput } from '@angular-devkit/architect'; import assert from 'node:assert'; -import path from 'node:path'; -import type { InlineConfig, Vitest } from 'vitest/node'; +import path, { join } from 'node:path'; +import type { Vitest } from 'vitest/node'; +import { + DevServerExternalResultMetadata, + updateExternalMetadata, +} from '../../../../tools/vite/utils'; import { assertIsError } from '../../../../utils/error'; -import { toPosixPath } from '../../../../utils/path'; import { type FullResult, type IncrementalResult, @@ -21,9 +24,8 @@ import { import { NormalizedUnitTestBuilderOptions } from '../../options'; import type { TestExecutor } from '../api'; import { setupBrowserConfiguration } from './browser-provider'; -import { createVitestPlugins } from './plugins'; - -type VitestCoverageOption = Exclude; +import { findVitestBaseConfig } from './configuration'; +import { createVitestConfigPlugin, createVitestPlugins } from './plugins'; export class VitestExecutor implements TestExecutor { private vitest: Vitest | undefined; @@ -31,6 +33,12 @@ export class VitestExecutor implements TestExecutor { private readonly projectName: string; private readonly options: NormalizedUnitTestBuilderOptions; private readonly buildResultFiles = new Map(); + private readonly externalMetadata: DevServerExternalResultMetadata = { + implicitBrowser: [], + implicitServer: [], + explicitBrowser: [], + explicitServer: [], + }; // This is a reverse map of the entry points created in `build-options.ts`. // It is used by the in-memory provider plugin to map the requested test file @@ -72,6 +80,8 @@ export class VitestExecutor implements TestExecutor { } } + updateExternalMetadata(buildResult, this.externalMetadata, undefined, true); + // Initialize Vitest if not already present. this.vitest ??= await this.initializeVitest(); const vitest = this.vitest; @@ -87,7 +97,9 @@ export class VitestExecutor implements TestExecutor { if (source) { modifiedSourceFiles.add(source); } - vitest.invalidateFile(toPosixPath(path.join(this.options.workspaceRoot, modifiedFile))); + vitest.invalidateFile( + this.normalizePath(path.join(this.options.workspaceRoot, modifiedFile)), + ); } const specsToRerun = []; @@ -137,7 +149,14 @@ export class VitestExecutor implements TestExecutor { debug, watch, browserViewport, + ui, + projectRoot, + runnerConfig, + projectSourceRoot, + cacheOptions, } = this.options; + const projectName = this.projectName; + let vitestNodeModule; try { vitestNodeModule = await import('vitest/node'); @@ -156,7 +175,7 @@ export class VitestExecutor implements TestExecutor { const browserOptions = await setupBrowserConfiguration( browsers, debug, - this.options.projectSourceRoot, + projectSourceRoot, browserViewport, ); if (browserOptions.errors?.length) { @@ -169,12 +188,10 @@ export class VitestExecutor implements TestExecutor { ); const testSetupFiles = this.prepareSetupFiles(); - const plugins = createVitestPlugins(this.options, testSetupFiles, browserOptions, { + const projectPlugins = createVitestPlugins({ workspaceRoot, - projectSourceRoot: this.options.projectSourceRoot, - projectName: this.projectName, - include: this.options.include, - exclude: this.options.exclude, + projectSourceRoot, + projectName, buildResultFiles: this.buildResultFiles, testFileToEntryPoint: this.testFileToEntryPoint, }); @@ -187,73 +204,50 @@ export class VitestExecutor implements TestExecutor { } : {}; + const externalConfigPath = + runnerConfig === true + ? await findVitestBaseConfig([projectRoot, workspaceRoot]) + : runnerConfig; + return startVitest( 'test', undefined, { - // Disable configuration file resolution/loading - config: false, + config: externalConfigPath, root: workspaceRoot, - project: ['base', this.projectName], - name: 'base', - include: [], - testNamePattern: this.options.filter, - reporters: reporters ?? ['default'], + project: projectName, outputFile, + cache: cacheOptions.enabled ? undefined : false, + testNamePattern: this.options.filter, watch, - coverage: await generateCoverageOption(coverage, this.projectName), + ui, ...debugOptions, }, { + // Note `.vitest` is auto appended to the path. + cacheDir: cacheOptions.path, server: { // Disable the actual file watcher. The boolean watch option above should still // be enabled as it controls other internal behavior related to rerunning tests. watch: null, }, - plugins, + plugins: [ + await createVitestConfigPlugin({ + browser: browserOptions.browser, + coverage, + projectName, + projectSourceRoot, + optimizeDepsInclude: this.externalMetadata.implicitBrowser, + reporters, + setupFiles: testSetupFiles, + projectPlugins, + include: [...this.testFileToEntryPoint.keys()].filter( + // Filter internal entries + (entry) => !entry.startsWith('angular:'), + ), + }), + ], }, ); } } - -async function generateCoverageOption( - coverage: NormalizedUnitTestBuilderOptions['coverage'], - projectName: string, -): Promise { - if (!coverage) { - return { - enabled: false, - }; - } - - let defaultExcludes: string[] = []; - if (coverage.exclude) { - try { - const vitestConfig = await import('vitest/config'); - defaultExcludes = vitestConfig.coverageConfigDefaults.exclude; - } catch {} - } - - return { - enabled: true, - excludeAfterRemap: true, - include: coverage.include, - reportsDirectory: toPosixPath(path.join('coverage', projectName)), - thresholds: coverage.thresholds, - watermarks: coverage.watermarks, - // Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures - ...(coverage.exclude - ? { - exclude: [ - // Augment the default exclude https://vitest.dev/config/#coverage-exclude - // with the user defined exclusions - ...coverage.exclude, - ...defaultExcludes, - ], - } - : {}), - ...(coverage.reporters - ? ({ reporter: coverage.reporters } satisfies VitestCoverageOption) - : {}), - }; -} diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/index.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/index.ts index 2b76c5fd7456..fed814bdd78e 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/index.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/index.ts @@ -23,16 +23,24 @@ const VitestTestRunner: TestRunner = { checker.check('vitest'); if (options.browsers?.length) { + if (process.versions.webcontainer) { + checker.check('@vitest/browser-preview'); + } else { + checker.checkAny( + ['@vitest/browser-playwright', '@vitest/browser-webdriverio', '@vitest/browser-preview'], + 'The "browsers" option requires either ' + + '"@vitest/browser-playwright", "@vitest/browser-webdriverio", or "@vitest/browser-preview" to be installed.', + ); + } + } else { + // DOM emulation is used when no browsers are specified checker.checkAny( - ['playwright', 'webdriverio'], - 'The "browsers" option requires either "playwright" or "webdriverio" to be installed.', + ['jsdom', 'happy-dom'], + 'A DOM environment is required for non-browser tests. Please install either "jsdom" or "happy-dom".', ); - } else { - // JSDOM is used when no browsers are specified - checker.check('jsdom'); } - if (options.coverage) { + if (options.coverage.enabled) { checker.check('@vitest/coverage-v8'); } @@ -47,6 +55,18 @@ const VitestTestRunner: TestRunner = { const projectName = context.target?.project; assert(projectName, 'The builder requires a target.'); + if (!!process.versions.webcontainer && options.browsers?.length) { + context.logger.info( + `Webcontainer environment detected. Using '@vitest/browser-preview' for browser-based tests.`, + ); + } + + if (typeof options.runnerConfig === 'string') { + context.logger.info(`Using Vitest configuration file: ${options.runnerConfig}`); + } else if (options.runnerConfig) { + context.logger.info('Automatically searching for and using Vitest configuration file.'); + } + return new VitestExecutor(projectName, options, testEntryPointMappings); }, }; diff --git a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts index df7d2e06449f..93ef7cfb9f85 100644 --- a/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts +++ b/packages/angular/build/src/builders/unit-test/runners/vitest/plugins.ts @@ -8,13 +8,19 @@ import assert from 'node:assert'; import { readFile } from 'node:fs/promises'; +import { createRequire } from 'node:module'; +import { platform } from 'node:os'; import path from 'node:path'; -import type { VitestPlugin } from 'vitest/node'; +import type { + BrowserConfigOptions, + InlineConfig, + UserWorkspaceConfig, + VitestPlugin, +} from 'vitest/node'; import { createBuildAssetsMiddleware } from '../../../../tools/vite/middlewares/assets-middleware'; import { toPosixPath } from '../../../../utils/path'; import type { ResultFile } from '../../../application/results'; import type { NormalizedUnitTestBuilderOptions } from '../../options'; -import type { BrowserConfiguration } from './browser-provider'; type VitestPlugins = Awaited>; @@ -22,140 +28,315 @@ interface PluginOptions { workspaceRoot: string; projectSourceRoot: string; projectName: string; - include?: string[]; - exclude?: string[]; buildResultFiles: ReadonlyMap; testFileToEntryPoint: ReadonlyMap; } -export function createVitestPlugins( - options: NormalizedUnitTestBuilderOptions, - testSetupFiles: string[], - browserOptions: BrowserConfiguration, - pluginOptions: PluginOptions, -): VitestPlugins { - const { workspaceRoot, projectName, buildResultFiles, testFileToEntryPoint } = pluginOptions; +type VitestCoverageOption = Exclude; + +interface VitestConfigPluginOptions { + browser: BrowserConfigOptions | undefined; + coverage: NormalizedUnitTestBuilderOptions['coverage']; + projectName: string; + projectSourceRoot: string; + reporters?: string[] | [string, object][]; + setupFiles: string[]; + projectPlugins: Exclude; + include: string[]; + optimizeDepsInclude: string[]; +} + +async function findTestEnvironment( + projectResolver: NodeJS.RequireResolve, +): Promise<'jsdom' | 'happy-dom'> { + try { + projectResolver('happy-dom'); + + return 'happy-dom'; + } catch { + // happy-dom is not installed, fallback to jsdom + return 'jsdom'; + } +} + +export async function createVitestConfigPlugin( + options: VitestConfigPluginOptions, +): Promise { + const { + include, + browser, + projectName, + reporters, + setupFiles, + projectPlugins, + projectSourceRoot, + } = options; + + const { mergeConfig } = await import('vitest/config'); + + return { + name: 'angular:vitest-configuration', + async config(config) { + const testConfig = config.test; + + if (testConfig?.projects?.length) { + this.warn( + 'The "test.projects" option in the Vitest configuration file is not supported. ' + + 'The Angular CLI Test system will construct its own project configuration.', + ); + delete testConfig.projects; + } + + if (testConfig?.include) { + this.warn( + 'The "test.include" option in the Vitest configuration file is not supported. ' + + 'The Angular CLI Test system will manage test file discovery.', + ); + delete testConfig.include; + } + + // Merge user-defined plugins from the Vitest config with the CLI's internal plugins. + if (config.plugins) { + const userPlugins = config.plugins.filter( + (plugin) => + // Only inspect objects with a `name` property as these would be the internal injected plugins + !plugin || + typeof plugin !== 'object' || + !('name' in plugin) || + (!plugin.name.startsWith('angular:') && !plugin.name.startsWith('vitest')), + ); + + if (userPlugins.length > 0) { + projectPlugins.push(...userPlugins); + } + delete config.plugins; + } + + const projectResolver = createRequire(projectSourceRoot + '/').resolve; + + const projectDefaults: UserWorkspaceConfig = { + test: { + setupFiles, + globals: true, + // Default to `false` to align with the Karma/Jasmine experience. + isolate: false, + sequence: { setupFiles: 'list' }, + }, + optimizeDeps: { + noDiscovery: true, + include: options.optimizeDepsInclude, + }, + resolve: { + mainFields: ['es2020', 'module', 'main'], + conditions: ['es2015', 'es2020', 'module'], + }, + }; + + const { optimizeDeps, resolve } = config; + const projectOverrides: UserWorkspaceConfig = { + test: { + name: projectName, + include, + // CLI provider browser options override, if present + ...(browser ? { browser } : {}), + // If the user has not specified an environment, use a smart default. + ...(!testConfig?.environment + ? { environment: await findTestEnvironment(projectResolver) } + : {}), + }, + plugins: projectPlugins, + optimizeDeps, + resolve, + }; + + const projectBase = mergeConfig(projectDefaults, testConfig ? { test: testConfig } : {}); + const projectConfig = mergeConfig(projectBase, projectOverrides); + + return { + test: { + coverage: await generateCoverageOption(options.coverage, projectName), + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ...(reporters ? ({ reporters } as any) : {}), + projects: [projectConfig], + }, + }; + }, + }; +} + +async function loadResultFile(file: ResultFile): Promise { + if (file.origin === 'memory') { + return new TextDecoder('utf-8').decode(file.contents); + } + + return readFile(file.inputPath, 'utf-8'); +} + +export function createVitestPlugins(pluginOptions: PluginOptions): VitestPlugins { + const { workspaceRoot, buildResultFiles, testFileToEntryPoint } = pluginOptions; + const isWindows = platform() === 'win32'; return [ { - name: 'angular:project-init', - // Type is incorrect. This allows a Promise. - // eslint-disable-next-line @typescript-eslint/no-misused-promises - configureVitest: async (context) => { - // Create a subproject that can be configured with plugins for browser mode. - // Plugins defined directly in the vite overrides will not be present in the - // browser specific Vite instance. - await context.injectTestProjects({ - test: { - name: projectName, - root: workspaceRoot, - globals: true, - setupFiles: testSetupFiles, - // Use `jsdom` if no browsers are explicitly configured. - // `node` is effectively no "environment" and the default. - environment: browserOptions.browser ? 'node' : 'jsdom', - browser: browserOptions.browser, - include: options.include, - ...(options.exclude ? { exclude: options.exclude } : {}), - }, - plugins: [ - { - name: 'angular:test-in-memory-provider', - enforce: 'pre', - resolveId: (id, importer) => { - if (importer && (id[0] === '.' || id[0] === '/')) { - let fullPath; - if (testFileToEntryPoint.has(importer)) { - fullPath = toPosixPath(path.join(workspaceRoot, id)); - } else { - fullPath = toPosixPath(path.join(path.dirname(importer), id)); - } - - const relativePath = path.relative(workspaceRoot, fullPath); - if (buildResultFiles.has(toPosixPath(relativePath))) { - return fullPath; - } - } - - if (testFileToEntryPoint.has(id)) { - return id; - } - - assert(buildResultFiles.size > 0, 'buildResult must be available for resolving.'); - const relativePath = path.relative(workspaceRoot, id); - if (buildResultFiles.has(toPosixPath(relativePath))) { - return id; - } - }, - load: async (id) => { - assert( - buildResultFiles.size > 0, - 'buildResult must be available for in-memory loading.', - ); - - // Attempt to load as a source test file. - const entryPoint = testFileToEntryPoint.get(id); - let outputPath; - if (entryPoint) { - outputPath = entryPoint + '.js'; - - // To support coverage exclusion of the actual test file, the virtual - // test entry point only references the built and bundled intermediate file. - return { - code: `import "./${outputPath}";`, - }; - } else { - // Attempt to load as a built artifact. - const relativePath = path.relative(workspaceRoot, id); - outputPath = toPosixPath(relativePath); - } - - const outputFile = buildResultFiles.get(outputPath); - if (outputFile) { - const sourceMapPath = outputPath + '.map'; - const sourceMapFile = buildResultFiles.get(sourceMapPath); - const code = - outputFile.origin === 'memory' - ? Buffer.from(outputFile.contents).toString('utf-8') - : await readFile(outputFile.inputPath, 'utf-8'); - const map = sourceMapFile - ? sourceMapFile.origin === 'memory' - ? Buffer.from(sourceMapFile.contents).toString('utf-8') - : await readFile(sourceMapFile.inputPath, 'utf-8') - : undefined; - - return { - code, - map: map ? JSON.parse(map) : undefined, - }; - } - }, - configureServer: (server) => { - server.middlewares.use( - createBuildAssetsMiddleware(server.config.base, buildResultFiles), - ); - }, - }, + name: 'angular:test-in-memory-provider', + enforce: 'pre', + resolveId: (id, importer) => { + // Fast path for test entry points. + if (testFileToEntryPoint.has(id)) { + return id; + } + + // Workaround for Vitest in Windows when a fully qualified absolute path is provided with + // a superfluous leading slash. This can currently occur with the `@vitest/coverage-v8` provider + // when it uses `removeStartsWith(url, FILE_PROTOCOL)` to convert a file URL resulting in + // `/D:/tmp_dir/...` instead of `D:/tmp_dir/...`. + if (id[0] === '/' && isWindows) { + const slicedId = id.slice(1); + if (path.isAbsolute(slicedId)) { + return slicedId; + } + } + + if (importer && (id[0] === '.' || id[0] === '/')) { + let fullPath; + if (testFileToEntryPoint.has(importer)) { + fullPath = toPosixPath(path.join(workspaceRoot, id)); + } else { + fullPath = toPosixPath(path.join(path.dirname(importer), id)); + } + + const relativePath = path.relative(workspaceRoot, fullPath); + if (buildResultFiles.has(toPosixPath(relativePath))) { + return fullPath; + } + } + + // Determine the base directory for resolution. + let baseDir: string; + if (importer) { + // If the importer is a test entry point, resolve relative to the workspace root. + // Otherwise, resolve relative to the importer's directory. + baseDir = testFileToEntryPoint.has(importer) ? workspaceRoot : path.dirname(importer); + } else { + // If there's no importer, assume the id is relative to the workspace root. + baseDir = workspaceRoot; + } + + // Construct the full, absolute path and normalize it to POSIX format. + const fullPath = toPosixPath(path.join(baseDir, id)); + + // Check if the resolved path corresponds to a known build artifact. + const relativePath = path.relative(workspaceRoot, fullPath); + if (buildResultFiles.has(toPosixPath(relativePath))) { + return fullPath; + } + + // If the module cannot be resolved from the build artifacts, let other plugins handle it. + return undefined; + }, + load: async (id) => { + assert(buildResultFiles.size > 0, 'buildResult must be available for in-memory loading.'); + + // Attempt to load as a source test file. + const entryPoint = testFileToEntryPoint.get(id); + let outputPath; + if (entryPoint) { + outputPath = entryPoint + '.js'; + + // To support coverage exclusion of the actual test file, the virtual + // test entry point only references the built and bundled intermediate file. + return { + code: `import "./${outputPath}";`, + }; + } else { + // Attempt to load as a built artifact. + const relativePath = path.relative(workspaceRoot, id); + outputPath = toPosixPath(relativePath); + } + + const outputFile = buildResultFiles.get(outputPath); + if (outputFile) { + const code = await loadResultFile(outputFile); + const sourceMapPath = outputPath + '.map'; + const sourceMapFile = buildResultFiles.get(sourceMapPath); + const sourceMapText = sourceMapFile ? await loadResultFile(sourceMapFile) : undefined; + + // Vitest will include files in the coverage report if the sourcemap contains no sources. + // For builder-internal generated code chunks, which are typically helper functions, + // a virtual source is added to the sourcemap to prevent them from being incorrectly + // included in the final coverage report. + const map = sourceMapText ? JSON.parse(sourceMapText) : undefined; + if (map) { + if (!map.sources?.length && !map.sourcesContent?.length && !map.mappings) { + map.sources = ['virtual:builder']; + } + } + + return { + code, + map, + }; + } + }, + configureServer: (server) => { + server.middlewares.use(createBuildAssetsMiddleware(server.config.base, buildResultFiles)); + }, + }, + { + name: 'angular:html-index', + transformIndexHtml: () => { + // Add all global stylesheets + if (buildResultFiles.has('styles.css')) { + return [ { - name: 'angular:html-index', - transformIndexHtml: () => { - // Add all global stylesheets - if (buildResultFiles.has('styles.css')) { - return [ - { - tag: 'link', - attrs: { href: 'styles.css', rel: 'stylesheet' }, - injectTo: 'head', - }, - ]; - } - - return []; - }, + tag: 'link', + attrs: { href: 'styles.css', rel: 'stylesheet' }, + injectTo: 'head', }, - ], - }); + ]; + } + + return []; }, }, ]; } + +async function generateCoverageOption( + coverage: NormalizedUnitTestBuilderOptions['coverage'], + projectName: string, +): Promise { + let defaultExcludes: string[] = []; + if (coverage.exclude) { + try { + const vitestConfig = await import('vitest/config'); + defaultExcludes = vitestConfig.coverageConfigDefaults.exclude; + } catch {} + } + + return { + enabled: coverage.enabled, + excludeAfterRemap: true, + // Vitest performs a pre-check and a post-check for sourcemaps. + // The pre-check uses the bundled files, so specific bundled entry points and chunks need to be included. + // The post-check uses the original source files, so the user's include is used. + ...(coverage.include ? { include: ['spec-*.js', 'chunk-*.js', ...coverage.include] } : {}), + reportsDirectory: toPosixPath(path.join('coverage', projectName)), + thresholds: coverage.thresholds, + watermarks: coverage.watermarks, + // Special handling for `exclude`/`reporters` due to an undefined value causing upstream failures + ...(coverage.exclude + ? { + exclude: [ + // Augment the default exclude https://vitest.dev/config/#coverage-exclude + // with the user defined exclusions + ...coverage.exclude, + ...defaultExcludes, + ], + } + : {}), + ...(coverage.reporters + ? ({ reporter: coverage.reporters } satisfies VitestCoverageOption) + : {}), + }; +} diff --git a/packages/angular/build/src/builders/unit-test/schema.json b/packages/angular/build/src/builders/unit-test/schema.json index d3ae766d05b6..3b92d31a4cde 100644 --- a/packages/angular/build/src/builders/unit-test/schema.json +++ b/packages/angular/build/src/builders/unit-test/schema.json @@ -19,6 +19,11 @@ "default": "vitest", "enum": ["karma", "vitest"] }, + "runnerConfig": { + "type": ["string", "boolean"], + "description": "Specifies the configuration file for the selected test runner. If a string is provided, it will be used as the path to the configuration file. If `true`, the builder will search for a default configuration file (e.g., `vitest.config.ts` or `karma.conf.js`). If `false`, no external configuration file will be used.\\nFor Vitest, this enables advanced options and the use of custom plugins. Please note that while the file is loaded, the Angular team does not provide direct support for its specific contents or any third-party plugins used within it.", + "default": false + }, "browsers": { "description": "Specifies the browsers to use for test execution. When not specified, tests are run in a Node.js environment using jsdom. For both Vitest and Karma, browser names ending with 'Headless' (e.g., 'ChromeHeadless') will enable headless mode.", "type": "array", @@ -60,6 +65,11 @@ "description": "Enables debugging mode for tests, allowing the use of the Node Inspector.", "default": false }, + "ui": { + "type": "boolean", + "description": "Enables the Vitest UI for interactive test execution. This option is only available for the Vitest runner.", + "default": false + }, "coverage": { "type": "boolean", "description": "Enables coverage reporting for tests.", diff --git a/packages/angular/build/src/builders/unit-test/test-discovery.ts b/packages/angular/build/src/builders/unit-test/test-discovery.ts index 987b55e39a81..47bfed884ad3 100644 --- a/packages/angular/build/src/builders/unit-test/test-discovery.ts +++ b/packages/angular/build/src/builders/unit-test/test-discovery.ts @@ -7,6 +7,7 @@ */ import { type PathLike, constants, promises as fs } from 'node:fs'; +import os from 'node:os'; import { basename, dirname, extname, isAbsolute, join, relative } from 'node:path'; import { glob, isDynamicPattern } from 'tinyglobby'; import { toPosixPath } from '../../utils/path'; @@ -64,7 +65,7 @@ export async function findTests( }); for (const match of globMatches) { - resolvedTestFiles.add(match); + resolvedTestFiles.add(toPosixPath(match)); } } @@ -157,15 +158,32 @@ function generateNameFromPath( return result; } -/** Removes a leading slash from a path. */ -const removeLeadingSlash = (path: string): string => { - return path.startsWith('/') ? path.substring(1) : path; -}; +/** + * Whether the current operating system's filesystem is case-insensitive. + */ +const isCaseInsensitiveFilesystem = os.platform() === 'win32' || os.platform() === 'darwin'; -/** Removes a prefix from the beginning of a string. */ -const removePrefix = (str: string, prefix: string): string => { - return str.startsWith(prefix) ? str.substring(prefix.length) : str; -}; +/** + * Removes a prefix from the beginning of a string, with conditional case-insensitivity + * based on the operating system's filesystem characteristics. + * + * @param text The string to remove the prefix from. + * @param prefix The prefix to remove. + * @returns The string with the prefix removed, or the original string if the prefix was not found. + */ +function removePrefix(text: string, prefix: string): string { + if (isCaseInsensitiveFilesystem) { + if (text.toLowerCase().startsWith(prefix.toLowerCase())) { + return text.substring(prefix.length); + } + } else { + if (text.startsWith(prefix)) { + return text.substring(prefix.length); + } + } + + return text; +} /** * Removes potential root paths from a file path, returning a relative path. @@ -177,8 +195,10 @@ const removePrefix = (str: string, prefix: string): string => { */ function removeRoots(path: string, roots: string[]): string { for (const root of roots) { - if (path.startsWith(root)) { - return path.substring(root.length); + const result = removePrefix(path, root); + // If the prefix was removed, the result will be a different string. + if (result !== path) { + return result; } } @@ -194,15 +214,18 @@ function removeRoots(path: string, roots: string[]): string { * @returns A normalized glob pattern. */ function normalizePattern(pattern: string, projectRootPrefix: string): string { - let normalizedPattern = toPosixPath(pattern); - normalizedPattern = removeLeadingSlash(normalizedPattern); + const posixPattern = toPosixPath(pattern); + + // Do not modify absolute paths. The globber will handle them correctly. + if (isAbsolute(posixPattern)) { + return posixPattern; + } - // Some IDEs and tools may provide patterns relative to the workspace root. - // To ensure the glob operates correctly within the project's source root, - // we remove the project's relative path from the front of the pattern. - normalizedPattern = removePrefix(normalizedPattern, projectRootPrefix); + // For relative paths, ensure they are correctly relative to the project source root. + // This involves removing the project root prefix if the user provided a workspace-relative path. + const normalizedRelative = removePrefix(posixPattern, projectRootPrefix); - return normalizedPattern; + return normalizedRelative; } /** @@ -238,15 +261,15 @@ async function resolveStaticPattern( for (const infix of TEST_FILE_INFIXES) { const potentialSpec = join(dirname(fullPath), `${baseName}${infix}${fileExt}`); if (await exists(potentialSpec)) { - return { resolved: [potentialSpec], unresolved: [] }; + return { resolved: [toPosixPath(potentialSpec)], unresolved: [] }; } } if (await exists(fullPath)) { - return { resolved: [fullPath], unresolved: [] }; + return { resolved: [toPosixPath(fullPath)], unresolved: [] }; } - return { resolved: [], unresolved: [pattern] }; + return { resolved: [], unresolved: [toPosixPath(pattern)] }; } /** Checks if a path exists and is a directory. */ diff --git a/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts new file mode 100644 index 000000000000..f68dd8daa171 --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/tests/behavior/runner-config-vitest_spec.ts @@ -0,0 +1,353 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { execute } from '../../index'; +import { + BASE_OPTIONS, + describeBuilder, + UNIT_TEST_BUILDER_INFO, + setupApplicationTarget, +} from '../setup'; + +const VITEST_CONFIG_CONTENT = ` +import { defineConfig } from 'vitest/config'; +export default defineConfig({ + test: { + reporters: [['junit', { outputFile: './vitest-results.xml' }]], + }, +}); +`; + +describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { + describe('Behavior: "runnerConfig with Vitest runner"', () => { + beforeEach(() => { + setupApplicationTarget(harness); + }); + + it('should use custom reporters defined in runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toExist(); + }); + + it('should exclude test files based on runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + exclude: ['src/app/app.component.spec.ts'], + reporters: ['default', ['json', { outputFile: 'vitest-results.json' }]], + }, + }); + `, + ); + + // Create a second test file that should be executed + harness.writeFile( + 'src/app/app-second.spec.ts', + ` + import { TestBed } from '@angular/core/testing'; + import { AppComponent } from './app.component'; + + describe('AppComponent', () => { + beforeEach(() => TestBed.configureTestingModule({ + declarations: [AppComponent], + })); + + it('should create the app', () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app).toBeTruthy(); + }); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const results = JSON.parse(harness.readFile('vitest-results.json')); + expect(results.numPassedTests).toBe(1); + }); + + it('should allow overriding globals to false via runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + globals: false, + }, + }); + `, + ); + + // This test will fail if globals are enabled, because `test` will not be defined. + harness.writeFile( + 'src/app/app.component.spec.ts', + ` + import { expect } from 'vitest'; + test('should pass', () => { + expect(true).toBe(true); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeFalse(); + }); + + it('should initialize environment even when globals are disabled in runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + globals: false, + }, + }); + `, + ); + + harness.writeFile( + 'src/app/app.component.spec.ts', + ` + import { test, expect } from 'vitest'; + test('should pass', () => { + expect(true).toBe(true); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + + it('should fail when a DOM-dependent test is run in a node environment', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + environment: 'node', + }, + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeFalse(); + }); + + it('should warn and ignore "test.projects" option from runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + projects: ['./foo.config.ts'], + }, + }); + `, + ); + + const { result, logs } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + // TODO: Re-enable once Vite logs are remapped through build system + // expect(logs).toContain( + // jasmine.objectContaining({ + // level: 'warn', + // message: jasmine.stringMatching( + // 'The "test.projects" option in the Vitest configuration file is not supported.', + // ), + // }), + // ); + }); + + it('should warn and ignore "test.include" option from runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + include: ['src/app/non-existent.spec.ts'], + }, + }); + `, + ); + + const { result, logs } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + // TODO: Re-enable once Vite logs are remapped through build system + // expect(logs).toContain( + // jasmine.objectContaining({ + // level: 'warn', + // message: jasmine.stringMatching( + // 'The "test.include" option in the Vitest configuration file is not supported.', + // ), + // }), + // ); + }); + + it(`should append "test.setupFiles" (string) from runnerConfig to the CLI's setup`, async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + setupFiles: './src/app/custom-setup.ts', + }, + }); + `, + ); + + harness.writeFile('src/app/custom-setup.ts', `(globalThis as any).customSetupLoaded = true;`); + + harness.writeFile( + 'src/app/app.component.spec.ts', + ` + import { test, expect } from 'vitest'; + test('should have custom setup loaded', () => { + expect((globalThis as any).customSetupLoaded).toBe(true); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + + it(`should append "test.setupFiles" (array) from runnerConfig to the CLI's setup`, async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + setupFiles: ['./src/app/custom-setup-1.ts', './src/app/custom-setup-2.ts'], + }, + }); + `, + ); + + harness.writeFile('src/app/custom-setup-1.ts', `(globalThis as any).customSetup1 = true;`); + harness.writeFile('src/app/custom-setup-2.ts', `(globalThis as any).customSetup2 = true;`); + + harness.writeFile( + 'src/app/app.component.spec.ts', + ` + import { test, expect } from 'vitest'; + test('should have custom setups loaded', () => { + expect((globalThis as any).customSetup1).toBe(true); + expect((globalThis as any).customSetup2).toBe(true); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + + it('should merge and apply custom Vite plugins from runnerConfig file', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'vitest.config.ts', + }); + + harness.writeFile( + 'vitest.config.ts', + ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + plugins: [ + { + name: 'my-custom-transform-plugin', + transform(code, id) { + if (code.includes('__PLACEHOLDER__')) { + return code.replace('__PLACEHOLDER__', 'transformed by custom plugin'); + } + }, + }, + ], + }); + `, + ); + + harness.writeFile( + 'src/app/app.component.spec.ts', + ` + import { test, expect } from 'vitest'; + test('should have been transformed by custom plugin', () => { + const placeholder = '__PLACEHOLDER__'; + expect(placeholder).toBe('transformed by custom plugin'); + }); + `, + ); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + }); + }); +}); diff --git a/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts index aa0b3c4368fa..afb7aee20d22 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/browsers_spec.ts @@ -16,53 +16,33 @@ import { } from '../setup'; describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { - xdescribe('Option: "browsers"', () => { + describe('Option: "browsers"', () => { beforeEach(async () => { setupApplicationTarget(harness); }); - it('should use jsdom when browsers is not provided', async () => { + it('should use DOM emulation when browsers is not provided', async () => { harness.useTarget('test', { ...BASE_OPTIONS, browsers: undefined, }); - const { result, logs } = await harness.executeOnce(); + const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); - expectLog(logs, 'Using jsdom in Node.js for test execution.'); }); - it('should fail when browsers is empty', async () => { - harness.useTarget('test', { - ...BASE_OPTIONS, - browsers: [], - }); - - await expectAsync(harness.executeOnce()).toBeRejectedWithError( - /must NOT have fewer than 1 items/, - ); - }); - - it('should launch a browser when provided', async () => { + it('should fail when a browser is requested but no provider is installed', async () => { harness.useTarget('test', { ...BASE_OPTIONS, browsers: ['chrome'], }); const { result, logs } = await harness.executeOnce(); - expect(result?.success).toBeTrue(); - expectLog(logs, /Starting browser "chrome"/); - }); - - it('should launch a browser in headless mode when specified', async () => { - harness.useTarget('test', { - ...BASE_OPTIONS, - browsers: ['chromeheadless'], - }); - - const { result, logs } = await harness.executeOnce(); - expect(result?.success).toBeTrue(); - expectLog(logs, /Starting browser "chrome" in headless mode/); + expect(result?.success).toBeFalse(); + expectLog( + logs, + `The "browsers" option requires either "@vitest/browser-playwright", "@vitest/browser-webdriverio", or "@vitest/browser-preview" to be installed`, + ); }); }); }); diff --git a/packages/angular/build/src/builders/unit-test/tests/options/code-coverage-include_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage-include_spec.ts new file mode 100644 index 000000000000..6cdbc8bfa23b --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage-include_spec.ts @@ -0,0 +1,82 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { execute } from '../../index'; +import { + BASE_OPTIONS, + describeBuilder, + UNIT_TEST_BUILDER_INFO, + setupApplicationTarget, +} from '../setup'; + +describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { + describe('Option: "coverageInclude"', () => { + beforeEach(async () => { + setupApplicationTarget(harness); + await harness.writeFiles({ + 'src/app/included.ts': `export const a = 1;`, + 'src/app/included.spec.ts': ` + import { a } from './included'; + describe('included', () => { + it('should work', () => { + expect(a).toBe(1); + }); + }); + `, + 'src/app/excluded.ts': `export const b = 2;`, + }); + }); + + it('should only include and report coverage for files that match the glob pattern', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + coverage: true, + coverageInclude: ['**/included.ts'], + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + + const summary = JSON.parse(harness.readFile('coverage/test/coverage-final.json')); + const summaryKeys = Object.keys(summary); + + const includedKey = summaryKeys.find((key) => key.endsWith('src/app/included.ts')); + const excludedKey = summaryKeys.find((key) => key.endsWith('src/app/excluded.ts')); + + // Check that the included file is in the report and the excluded one is not. + expect(includedKey).toBeDefined(); + expect(excludedKey).toBeUndefined(); + + // Check that the coverage data for the included file is valid. + const includedCoverage = summary[includedKey!]; + // The file has one statement, and it should have been executed once. + expect(includedCoverage.s['0']).toBe(1); + }); + + it('should only include referenced files when no include pattern is provided', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + coverage: true, + // coverageInclude is not provided, so only referenced files should be included. + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + const summary = JSON.parse(harness.readFile('coverage/test/coverage-final.json')); + const summaryKeys = Object.keys(summary); + + const includedKey = summaryKeys.find((key) => key.endsWith('src/app/included.ts')); + const excludedKey = summaryKeys.find((key) => key.endsWith('src/app/excluded.ts')); + + // The included file is referenced by its spec and should be in the report. + expect(includedKey).toBeDefined(); + // The excluded file is not referenced and should NOT be in the report. + expect(excludedKey).toBeUndefined(); + }); + }); +}); diff --git a/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts index 490f2deaba4b..f8a8acb60591 100644 --- a/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts +++ b/packages/angular/build/src/builders/unit-test/tests/options/code-coverage_spec.ts @@ -28,7 +28,7 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); - expect(harness.hasFile('coverage/test/index.html')).toBeFalse(); + harness.expectFile('coverage/test/index.html').toNotExist(); }); it('should generate a code coverage report when coverage is true', async () => { @@ -39,7 +39,19 @@ describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { const { result } = await harness.executeOnce(); expect(result?.success).toBeTrue(); - expect(harness.hasFile('coverage/test/index.html')).toBeTrue(); + harness.expectFile('coverage/test/index.html').toExist(); + }); + + it('should generate a code coverage report when coverage is true', async () => { + harness.useTarget('test', { + ...BASE_OPTIONS, + coverage: true, + coverageReporters: ['json'] as any, + }); + + const { result } = await harness.executeOnce(); + expect(result?.success).toBeTrue(); + harness.expectFile('coverage/test/coverage-final.json').content.toContain('app.component.ts'); }); }); }); diff --git a/packages/angular/build/src/builders/unit-test/tests/options/runner-config_spec.ts b/packages/angular/build/src/builders/unit-test/tests/options/runner-config_spec.ts new file mode 100644 index 000000000000..b061b88e990c --- /dev/null +++ b/packages/angular/build/src/builders/unit-test/tests/options/runner-config_spec.ts @@ -0,0 +1,129 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { execute } from '../../index'; +import { + BASE_OPTIONS, + describeBuilder, + UNIT_TEST_BUILDER_INFO, + setupApplicationTarget, +} from '../setup'; + +const VITEST_CONFIG_CONTENT = ` +import { defineConfig } from 'vitest/config'; +export default defineConfig({ + test: { + reporters: [['junit', { outputFile: './vitest-results.xml' }]], + }, +}); +`; + +describeBuilder(execute, UNIT_TEST_BUILDER_INFO, (harness) => { + describe('Option: "runnerConfig"', () => { + beforeEach(() => { + setupApplicationTarget(harness); + }); + + describe('Vitest Runner', () => { + it('should use a specified config file path', async () => { + harness.writeFile('custom-vitest.config.ts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: 'custom-vitest.config.ts', + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toExist(); + }); + + it('should search for a config file when `true`', async () => { + harness.writeFile('vitest-base.config.ts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: true, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toExist(); + }); + + it('should ignore config file when `false`', async () => { + harness.writeFile('vitest-base.config.ts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: false, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toNotExist(); + }); + + it('should ignore config file by default', async () => { + harness.writeFile('vitest-base.config.ts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toNotExist(); + }); + + it('should find and use a `vitest-base.config.mts` in the project root', async () => { + harness.writeFile('vitest-base.config.mts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: true, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toExist(); + }); + + it('should find and use a `vitest-base.config.js` in the workspace root', async () => { + // This file should be ignored because the new logic looks for `vitest-base.config.*`. + harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT); + // The workspace root is the directory containing the project root in the test harness. + harness.writeFile('vitest-base.config.js', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: true, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toExist(); + }); + + it('should fallback to in-memory config when no base config is found', async () => { + // This file should be ignored because the new logic looks for `vitest-base.config.*` + // and when `runnerConfig` is true, it should not fall back to the default search. + harness.writeFile('vitest.config.ts', VITEST_CONFIG_CONTENT); + harness.useTarget('test', { + ...BASE_OPTIONS, + runnerConfig: true, + }); + + const { result } = await harness.executeOnce(); + + expect(result?.success).toBeTrue(); + harness.expectFile('vitest-results.xml').toNotExist(); + }); + }); + }); +}); diff --git a/packages/angular/build/src/builders/unit-test/tests/setup.ts b/packages/angular/build/src/builders/unit-test/tests/setup.ts index db03c2b0b348..e6770e115789 100644 --- a/packages/angular/build/src/builders/unit-test/tests/setup.ts +++ b/packages/angular/build/src/builders/unit-test/tests/setup.ts @@ -14,7 +14,7 @@ import { ApplicationBuilderOptions as ApplicationSchema, buildApplication, } from '../../../builders/application'; -import { Schema } from '../schema'; +import { Runner, Schema } from '../schema'; // TODO: Consider using package.json imports field instead of relative path // after the switch to rules_js. @@ -61,7 +61,7 @@ export const UNIT_TEST_BUILDER_INFO = Object.freeze({ export const BASE_OPTIONS = Object.freeze({ buildTarget: 'test:build', tsConfig: 'src/tsconfig.spec.json', - runner: 'vitest' as any, + runner: Runner.Vitest, }); /** diff --git a/packages/angular/build/src/private.ts b/packages/angular/build/src/private.ts index 5368eb3574e5..175dd39530ae 100644 --- a/packages/angular/build/src/private.ts +++ b/packages/angular/build/src/private.ts @@ -35,6 +35,8 @@ export { transformSupportedBrowsersToTargets } from './tools/esbuild/utils'; export { SassWorkerImplementation } from './tools/sass/sass-service'; export { SourceFileCache } from './tools/esbuild/angular/source-file-cache'; +export { Cache } from './tools/esbuild/cache'; +export { LmdbCacheStore } from './tools/esbuild/lmdb-cache-store'; export { createJitResourceTransformer } from './tools/angular/transformers/jit-resource-transformer'; export { JavaScriptTransformer } from './tools/esbuild/javascript-transformer'; @@ -59,6 +61,7 @@ export function createCompilerPlugin( } export type { AngularCompilation } from './tools/angular/compilation'; +export { DiagnosticModes } from './tools/angular/compilation'; export { createAngularCompilation }; export { ComponentStylesheetBundler } from './tools/esbuild/angular/component-stylesheets'; diff --git a/packages/angular/build/src/tools/angular/angular-host.ts b/packages/angular/build/src/tools/angular/angular-host.ts index 38c096f67674..e98ebf49f3eb 100644 --- a/packages/angular/build/src/tools/angular/angular-host.ts +++ b/packages/angular/build/src/tools/angular/angular-host.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import assert from 'node:assert'; import { createHash } from 'node:crypto'; import nodePath from 'node:path'; diff --git a/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts b/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts index f22e8c813ecc..00a17ccc453e 100644 --- a/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/angular-compilation.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import type { PartialMessage } from 'esbuild'; import type ts from 'typescript'; import { convertTypeScriptDiagnostic } from '../../esbuild/angular/diagnostics'; @@ -76,6 +76,7 @@ export abstract class AngularCompilation { referencedFiles: readonly string[]; externalStylesheets?: ReadonlyMap; templateUpdates?: ReadonlyMap; + componentResourcesDependencies?: ReadonlyMap; }>; abstract emitAffectedFiles(): Iterable | Promise>; diff --git a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts index a340d602577e..06a3f6e8c8d6 100644 --- a/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/aot-compilation.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import assert from 'node:assert'; import { relative } from 'node:path'; import ts from 'typescript'; @@ -62,6 +62,7 @@ export class AotCompilation extends AngularCompilation { referencedFiles: readonly string[]; externalStylesheets?: ReadonlyMap; templateUpdates?: ReadonlyMap; + componentResourcesDependencies?: ReadonlyMap; }> { // Dynamically load the Angular compiler CLI package const { NgtscProgram, OptimizeFor } = await AngularCompilation.loadCompilerCli(); @@ -175,13 +176,15 @@ export class AotCompilation extends AngularCompilation { findAffectedFiles(typeScriptProgram, angularCompiler, usingBuildInfo), ); + const componentResourcesDependencies = new Map(); + // Get all files referenced in the TypeScript/Angular program including component resources const referencedFiles = typeScriptProgram .getSourceFiles() .filter((sourceFile) => !angularCompiler.ignoreForEmit.has(sourceFile)) .flatMap((sourceFile) => { const resourceDependencies = angularCompiler.getResourceDependencies(sourceFile); - + componentResourcesDependencies.set(sourceFile.fileName, resourceDependencies); // Also invalidate Angular diagnostics for a source file if component resources are modified if (this.#state && hostOptions.modifiedFiles?.size) { for (const resourceDependency of resourceDependencies) { @@ -212,6 +215,7 @@ export class AotCompilation extends AngularCompilation { referencedFiles, externalStylesheets: hostOptions.externalStylesheets, templateUpdates, + componentResourcesDependencies, }; } diff --git a/packages/angular/build/src/tools/angular/compilation/hmr-candidates.ts b/packages/angular/build/src/tools/angular/compilation/hmr-candidates.ts index e10b935907c0..cba0b18e7414 100644 --- a/packages/angular/build/src/tools/angular/compilation/hmr-candidates.ts +++ b/packages/angular/build/src/tools/angular/compilation/hmr-candidates.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import assert from 'node:assert'; import ts from 'typescript'; diff --git a/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts b/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts index d2876654a15e..4c529df0db08 100644 --- a/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/jit-compilation.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import assert from 'node:assert'; import ts from 'typescript'; import { profileSync } from '../../esbuild/profiling'; diff --git a/packages/angular/build/src/tools/angular/compilation/noop-compilation.ts b/packages/angular/build/src/tools/angular/compilation/noop-compilation.ts index a683271b287f..e2c597b4d315 100644 --- a/packages/angular/build/src/tools/angular/compilation/noop-compilation.ts +++ b/packages/angular/build/src/tools/angular/compilation/noop-compilation.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ -import type ng from '@angular/compiler-cli'; +import type * as ng from '@angular/compiler-cli'; import type ts from 'typescript'; import { AngularHostOptions } from '../angular-host'; import { AngularCompilation } from './angular-compilation'; diff --git a/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts b/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts index d67fbb9bd06c..b6eccf20e3db 100644 --- a/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts +++ b/packages/angular/build/src/tools/angular/compilation/parallel-worker.ts @@ -45,58 +45,63 @@ export async function initialize(request: InitRequest) { } }); - const { compilerOptions, referencedFiles, externalStylesheets, templateUpdates } = - await compilation.initialize( - request.tsconfig, - { - fileReplacements: request.fileReplacements, - sourceFileCache, - modifiedFiles: sourceFileCache.modifiedFiles, - transformStylesheet(data, containingFile, stylesheetFile, order, className) { - const requestId = randomUUID(); - const resultPromise = new Promise((resolve, reject) => - stylesheetRequests.set(requestId, [resolve, reject]), - ); - - request.stylesheetPort.postMessage({ - requestId, - data, - containingFile, - stylesheetFile, - order, - className, - }); - - return resultPromise; - }, - processWebWorker(workerFile, containingFile) { - Atomics.store(request.webWorkerSignal, 0, 0); - request.webWorkerPort.postMessage({ workerFile, containingFile }); - - Atomics.wait(request.webWorkerSignal, 0, 0); - const result = receiveMessageOnPort(request.webWorkerPort)?.message; - - if (result?.error) { - throw result.error; - } - - return result?.workerCodeFile ?? workerFile; - }, + const { + compilerOptions, + referencedFiles, + externalStylesheets, + templateUpdates, + componentResourcesDependencies, + } = await compilation.initialize( + request.tsconfig, + { + fileReplacements: request.fileReplacements, + sourceFileCache, + modifiedFiles: sourceFileCache.modifiedFiles, + transformStylesheet(data, containingFile, stylesheetFile, order, className) { + const requestId = randomUUID(); + const resultPromise = new Promise((resolve, reject) => + stylesheetRequests.set(requestId, [resolve, reject]), + ); + + request.stylesheetPort.postMessage({ + requestId, + data, + containingFile, + stylesheetFile, + order, + className, + }); + + return resultPromise; }, - (compilerOptions) => { - Atomics.store(request.optionsSignal, 0, 0); - request.optionsPort.postMessage(compilerOptions); + processWebWorker(workerFile, containingFile) { + Atomics.store(request.webWorkerSignal, 0, 0); + request.webWorkerPort.postMessage({ workerFile, containingFile }); - Atomics.wait(request.optionsSignal, 0, 0); - const result = receiveMessageOnPort(request.optionsPort)?.message; + Atomics.wait(request.webWorkerSignal, 0, 0); + const result = receiveMessageOnPort(request.webWorkerPort)?.message; if (result?.error) { throw result.error; } - return result?.transformedOptions ?? compilerOptions; + return result?.workerCodeFile ?? workerFile; }, - ); + }, + (compilerOptions) => { + Atomics.store(request.optionsSignal, 0, 0); + request.optionsPort.postMessage(compilerOptions); + + Atomics.wait(request.optionsSignal, 0, 0); + const result = receiveMessageOnPort(request.optionsPort)?.message; + + if (result?.error) { + throw result.error; + } + + return result?.transformedOptions ?? compilerOptions; + }, + ); return { externalStylesheets, @@ -109,6 +114,7 @@ export async function initialize(request: InitRequest) { sourceMap: compilerOptions.sourceMap, inlineSourceMap: compilerOptions.inlineSourceMap, }, + componentResourcesDependencies, }; } diff --git a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts index eb9daa3b6155..95e6694b728b 100644 --- a/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts +++ b/packages/angular/build/src/tools/esbuild/angular/compiler-plugin.ts @@ -73,11 +73,11 @@ export function createCompilerPlugin( // Initialize a worker pool for JavaScript transformations. // Webcontainers currently do not support this persistent cache store. - let cacheStore: import('../lmdb-cache-store').LmbdCacheStore | undefined; + let cacheStore: import('../lmdb-cache-store').LmdbCacheStore | undefined; if (pluginOptions.sourceFileCache?.persistentCachePath && !process.versions.webcontainer) { try { - const { LmbdCacheStore } = await import('../lmdb-cache-store'); - cacheStore = new LmbdCacheStore( + const { LmdbCacheStore } = await import('../lmdb-cache-store'); + cacheStore = new LmdbCacheStore( path.join(pluginOptions.sourceFileCache.persistentCachePath, 'angular-compiler.db'), ); } catch (e) { diff --git a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts index b17029f6c5e1..635faca8c82e 100644 --- a/packages/angular/build/src/tools/esbuild/application-code-bundle.ts +++ b/packages/angular/build/src/tools/esbuild/application-code-bundle.ts @@ -654,7 +654,7 @@ function getEsBuildCommonPolyfillsOptions( tryToResolvePolyfillsAsRelative: boolean, loadResultCache: LoadResultCache | undefined, ): BuildOptions | undefined { - const { jit, workspaceRoot, i18nOptions } = options; + const { jit, workspaceRoot, i18nOptions, externalPackages } = options; const buildOptions = getEsBuildCommonOptions(options); buildOptions.splitting = false; @@ -671,8 +671,10 @@ function getEsBuildCommonPolyfillsOptions( // Locale data should go first so that project provided polyfill code can augment if needed. let needLocaleDataPlugin = false; if (i18nOptions.shouldInline) { - // Remove localize polyfill as this is not needed for build time i18n. - polyfills = polyfills.filter((path) => !path.startsWith('@angular/localize')); + if (!externalPackages) { + // Remove localize polyfill when i18n inline transformation have been applied to all the packages. + polyfills = polyfills.filter((path) => !path.startsWith('@angular/localize')); + } // Add locale data for all active locales // TODO: Inject each individually within the inlining process itself @@ -686,7 +688,7 @@ function getEsBuildCommonPolyfillsOptions( needLocaleDataPlugin = true; } if (needLocaleDataPlugin) { - buildOptions.plugins.push(createAngularLocaleDataPlugin()); + buildOptions.plugins.unshift(createAngularLocaleDataPlugin()); } if (polyfills.length === 0) { diff --git a/packages/angular/build/src/tools/esbuild/i18n-inliner.ts b/packages/angular/build/src/tools/esbuild/i18n-inliner.ts index 365fbb300953..fcb439b84c5c 100644 --- a/packages/angular/build/src/tools/esbuild/i18n-inliner.ts +++ b/packages/angular/build/src/tools/esbuild/i18n-inliner.ts @@ -11,7 +11,7 @@ import { createHash } from 'node:crypto'; import { extname, join } from 'node:path'; import { WorkerPool } from '../../utils/worker-pool'; import { BuildOutputFile, BuildOutputFileType } from './bundler-context'; -import type { LmbdCacheStore } from './lmdb-cache-store'; +import type { LmdbCacheStore } from './lmdb-cache-store'; import { createOutputFile } from './utils'; /** @@ -39,7 +39,7 @@ export interface I18nInlinerOptions { export class I18nInliner { #cacheInitFailed = false; #workerPool: WorkerPool; - #cache: LmbdCacheStore | undefined; + #cache: LmdbCacheStore | undefined; readonly #localizeFiles: ReadonlyMap; readonly #unmodifiedFiles: Array; @@ -274,9 +274,9 @@ export class I18nInliner { // Initialize a persistent cache for i18n transformations. try { - const { LmbdCacheStore } = await import('./lmdb-cache-store'); + const { LmdbCacheStore } = await import('./lmdb-cache-store'); - this.#cache = new LmbdCacheStore(join(persistentCachePath, 'angular-i18n.db')); + this.#cache = new LmdbCacheStore(join(persistentCachePath, 'angular-i18n.db')); } catch { this.#cacheInitFailed = true; diff --git a/packages/angular/build/src/tools/esbuild/javascript-transformer.ts b/packages/angular/build/src/tools/esbuild/javascript-transformer.ts index b27b8a087b46..b728a0f599e2 100644 --- a/packages/angular/build/src/tools/esbuild/javascript-transformer.ts +++ b/packages/angular/build/src/tools/esbuild/javascript-transformer.ts @@ -125,7 +125,7 @@ export class JavaScriptTransformer { { // The below is disable as with Yarn PNP this causes build failures with the below message // `Unable to deserialize cloned data`. - transferList: process.versions.pnp ? undefined : [data.buffer as ArrayBuffer], + transferList: process.versions.pnp ? undefined : [data.buffer], }, )) as Uint8Array; diff --git a/packages/angular/build/src/tools/esbuild/lmdb-cache-store.ts b/packages/angular/build/src/tools/esbuild/lmdb-cache-store.ts index 7d03cb597738..dba108285342 100644 --- a/packages/angular/build/src/tools/esbuild/lmdb-cache-store.ts +++ b/packages/angular/build/src/tools/esbuild/lmdb-cache-store.ts @@ -9,7 +9,7 @@ import { RootDatabase, open } from 'lmdb'; import { Cache, CacheStore } from './cache'; -export class LmbdCacheStore implements CacheStore { +export class LmdbCacheStore implements CacheStore { readonly #cacheFileUrl; #db: RootDatabase | undefined; diff --git a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts index 414ec5ca13d1..f0a137f578f8 100644 --- a/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/assets-middleware.ts @@ -40,7 +40,7 @@ export function createAngularAssetsMiddleware( // The base of the URL is unused but required to parse the URL. const pathname = pathnameWithoutBasePath(req.url, server.config.base); const extension = extname(pathname); - const pathnameHasTrailingSlash = pathname[pathname.length - 1] === '/'; + const pathnameHasTrailingSlash = pathname.at(-1) === '/'; // Rewrite all build assets to a vite raw fs URL const asset = assets.get(pathname); diff --git a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts index 0d8ac5441f1d..4b0a8d8390f1 100644 --- a/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts +++ b/packages/angular/build/src/tools/vite/middlewares/ssr-middleware.ts @@ -37,20 +37,13 @@ export function createAngularSsrInternalMiddleware( // which must be processed by the runtime linker, even if they are not used. await import('@angular/compiler'); const { writeResponseToNodeResponse, createWebRequestFromNodeRequest } = (await import( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - '@angular/ssr/node' as any + '@angular/ssr/node' as string )) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } }); const { ɵgetOrCreateAngularServerApp } = (await server.ssrLoadModule('/main.server.mjs')) as { ɵgetOrCreateAngularServerApp: typeof getOrCreateAngularServerApp; }; - // `ɵgetOrCreateAngularServerApp` can be undefined right after an error. - // See: https://github.com/angular/angular-cli/issues/29907 - if (!ɵgetOrCreateAngularServerApp) { - return next(); - } - const angularServerApp = ɵgetOrCreateAngularServerApp({ allowStaticRouteRender: true, }); @@ -94,8 +87,7 @@ export async function createAngularSsrExternalMiddleware( await import('@angular/compiler'); const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } = (await import( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - '@angular/ssr/node' as any + '@angular/ssr/node' as string )) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } }); return function angularSsrExternalMiddleware( diff --git a/packages/angular/build/src/tools/vite/plugins/index.ts b/packages/angular/build/src/tools/vite/plugins/index.ts index ef697aa7395a..6c4cdd4496e4 100644 --- a/packages/angular/build/src/tools/vite/plugins/index.ts +++ b/packages/angular/build/src/tools/vite/plugins/index.ts @@ -10,3 +10,4 @@ export { createAngularMemoryPlugin } from './angular-memory-plugin'; export { createRemoveIdPrefixPlugin } from './id-prefix-plugin'; export { createAngularSetupMiddlewaresPlugin, ServerSsrMode } from './setup-middlewares-plugin'; export { createAngularSsrTransformPlugin } from './ssr-transform-plugin'; +export { createAngularServerSideSSLPlugin } from './ssr-ssl-plugin'; diff --git a/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts b/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts new file mode 100644 index 000000000000..0cde7f89ef0a --- /dev/null +++ b/packages/angular/build/src/tools/vite/plugins/ssr-ssl-plugin.ts @@ -0,0 +1,58 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { rootCertificates } from 'node:tls'; +import type { Plugin } from 'vite'; + +export function createAngularServerSideSSLPlugin(): Plugin { + return { + name: 'angular-ssr-ssl-plugin', + apply: 'serve', + async configureServer({ config, httpServer }) { + const { + ssr, + server: { https }, + } = config; + + if (!ssr || !https?.cert) { + return; + } + + if (httpServer && 'ALPNProtocols' in httpServer) { + // Force Vite to use HTTP/1.1 when SSR and SSL are enabled. + // This is required because the Express server used for SSR does not support HTTP/2. + // See: https://github.com/vitejs/vite/blob/46d3077f2b63771cc50230bc907c48f5773c00fb/packages/vite/src/node/http.ts#L126 + + // We directly set the `ALPNProtocols` on the HTTP server to override the default behavior. + // Passing `ALPNProtocols` in the TLS options would cause Node.js to automatically include `h2`. + // Additionally, using `ALPNCallback` is not an option as it is mutually exclusive with `ALPNProtocols`. + // See: https://github.com/nodejs/node/blob/b8b4350ed3b73d225eb9e628d69151df56eaf298/lib/internal/http2/core.js#L3351 + httpServer.ALPNProtocols = ['http/1.1']; + } + + // TODO(alanagius): Replace `undici` with `tls.setDefaultCACertificates` once we only support Node.js 22.18.0+ and 24.5.0+. + // See: https://nodejs.org/api/tls.html#tlssetdefaultcacertificatescerts + const { getGlobalDispatcher, setGlobalDispatcher, Agent } = await import('undici'); + const originalDispatcher = getGlobalDispatcher(); + const { cert } = https; + const certificates = Array.isArray(cert) ? cert : [cert]; + + setGlobalDispatcher( + new Agent({ + connect: { + ca: [...rootCertificates, ...certificates], + }, + }), + ); + + httpServer?.on('close', () => { + setGlobalDispatcher(originalDispatcher); + }); + }, + }; +} diff --git a/packages/angular/build/src/tools/vite/utils.ts b/packages/angular/build/src/tools/vite/utils.ts index 2322eb210bec..2f7cfba84306 100644 --- a/packages/angular/build/src/tools/vite/utils.ts +++ b/packages/angular/build/src/tools/vite/utils.ts @@ -7,8 +7,10 @@ */ import { lookup as lookupMimeType } from 'mrmime'; +import { builtinModules, isBuiltin } from 'node:module'; import { extname } from 'node:path'; import type { DepOptimizationConfig } from 'vite'; +import type { ExternalResultMetadata } from '../esbuild/bundler-execution-result'; import { JavaScriptTransformer } from '../esbuild/javascript-transformer'; import { getFeatureSupport } from '../esbuild/utils'; @@ -109,3 +111,61 @@ export function getDepOptimizationConfig({ }, }; } + +export interface DevServerExternalResultMetadata { + implicitBrowser: string[]; + implicitServer: string[]; + explicitBrowser: string[]; + explicitServer: string[]; +} + +export function isAbsoluteUrl(url: string): boolean { + try { + new URL(url); + + return true; + } catch { + return false; + } +} + +export function updateExternalMetadata( + result: { detail?: { externalMetadata?: ExternalResultMetadata } }, + externalMetadata: DevServerExternalResultMetadata, + externalDependencies: string[] | undefined, + explicitPackagesOnly: boolean = false, +): void { + if (!result.detail?.['externalMetadata']) { + return; + } + + const { implicitBrowser, implicitServer, explicit } = result.detail['externalMetadata']; + const implicitServerFiltered = implicitServer.filter((m) => !isBuiltin(m) && !isAbsoluteUrl(m)); + const implicitBrowserFiltered = implicitBrowser.filter((m) => !isAbsoluteUrl(m)); + const explicitBrowserFiltered = explicitPackagesOnly + ? explicit.filter((m) => !isAbsoluteUrl(m)) + : explicit; + + // Empty Arrays to avoid growing unlimited with every re-build. + externalMetadata.explicitBrowser.length = 0; + externalMetadata.explicitServer.length = 0; + externalMetadata.implicitServer.length = 0; + externalMetadata.implicitBrowser.length = 0; + + const externalDeps = externalDependencies ?? []; + externalMetadata.explicitBrowser.push(...explicitBrowserFiltered, ...externalDeps); + externalMetadata.explicitServer.push( + ...explicitBrowserFiltered, + ...externalDeps, + ...builtinModules, + ); + externalMetadata.implicitServer.push(...implicitServerFiltered); + externalMetadata.implicitBrowser.push(...implicitBrowserFiltered); + + // The below needs to be sorted as Vite uses these options as part of the hashing invalidation algorithm. + // See: https://github.com/vitejs/vite/blob/0873bae0cfe0f0718ad2f5743dd34a17e4ab563d/packages/vite/src/node/optimizer/index.ts#L1203-L1239 + externalMetadata.explicitBrowser.sort(); + externalMetadata.explicitServer.sort(); + externalMetadata.implicitServer.sort(); + externalMetadata.implicitBrowser.sort(); +} diff --git a/packages/angular/build/src/utils/check-port.ts b/packages/angular/build/src/utils/check-port.ts index 58e5a0a0f854..d7c04f0b9f72 100644 --- a/packages/angular/build/src/utils/check-port.ts +++ b/packages/angular/build/src/utils/check-port.ts @@ -32,7 +32,7 @@ export async function checkPort(port: number, host: string): Promise { return; } - if (!isTTY) { + if (!isTTY()) { reject(createInUseError(port)); return; diff --git a/packages/angular/build/src/utils/project-metadata.ts b/packages/angular/build/src/utils/project-metadata.ts index bc997652fef1..31912d5e9905 100644 --- a/packages/angular/build/src/utils/project-metadata.ts +++ b/packages/angular/build/src/utils/project-metadata.ts @@ -15,7 +15,7 @@ import { join } from 'node:path'; * @returns A normalized path string. */ export function normalizeDirectoryPath(path: string): string { - const last = path[path.length - 1]; + const last = path.at(-1); if (last === '/' || last === '\\') { return path.slice(0, -1); } diff --git a/packages/angular/build/src/utils/server-rendering/launch-server.ts b/packages/angular/build/src/utils/server-rendering/launch-server.ts index c17337178b13..95b2784c6f63 100644 --- a/packages/angular/build/src/utils/server-rendering/launch-server.ts +++ b/packages/angular/build/src/utils/server-rendering/launch-server.ts @@ -21,8 +21,7 @@ export const DEFAULT_URL = new URL('http://ng-localhost/'); export async function launchServer(): Promise { const { reqHandler } = await loadEsmModuleFromMemory('./server.mjs'); const { createWebRequestFromNodeRequest, writeResponseToNodeResponse } = (await import( - // eslint-disable-next-line @typescript-eslint/no-explicit-any - '@angular/ssr/node' as any + '@angular/ssr/node' as string )) as typeof import('@angular/ssr/node', { with: { 'resolution-mode': 'import' } }); if (!isSsrNodeRequestHandler(reqHandler) && !isSsrRequestHandler(reqHandler)) { diff --git a/packages/angular/build/src/utils/server-rendering/manifest.ts b/packages/angular/build/src/utils/server-rendering/manifest.ts index 2dfad0ff2dfb..b01bff38b58f 100644 --- a/packages/angular/build/src/utils/server-rendering/manifest.ts +++ b/packages/angular/build/src/utils/server-rendering/manifest.ts @@ -77,7 +77,7 @@ export function generateAngularServerAppEngineManifest( // Remove trailing slash but retain leading slash. let basePath = baseHref || '/'; - if (basePath.length > 1 && basePath[basePath.length - 1] === '/') { + if (basePath.length > 1 && basePath.at(-1) === '/') { basePath = basePath.slice(0, -1); } diff --git a/packages/angular/build/src/utils/server-rendering/prerender.ts b/packages/angular/build/src/utils/server-rendering/prerender.ts index e087262a7f0c..f33f851f10c4 100644 --- a/packages/angular/build/src/utils/server-rendering/prerender.ts +++ b/packages/angular/build/src/utils/server-rendering/prerender.ts @@ -14,7 +14,7 @@ import { BuildOutputFile, BuildOutputFileType } from '../../tools/esbuild/bundle import { BuildOutputAsset } from '../../tools/esbuild/bundler-execution-result'; import { assertIsError } from '../error'; import { toPosixPath } from '../path'; -import { urlJoin } from '../url'; +import { addLeadingSlash, addTrailingSlash, joinUrlParts, stripLeadingSlash } from '../url'; import { WorkerPool } from '../worker-pool'; import { IMPORT_EXEC_ARGV } from './esm-in-memory-loader/utils'; import { SERVER_APP_MANIFEST_FILENAME } from './manifest'; @@ -26,6 +26,7 @@ import { WritableSerializableRouteTreeNode, } from './models'; import type { RenderWorkerData } from './render-worker'; +import { generateRedirectStaticPage } from './utils'; type PrerenderOptions = NormalizedApplicationBuildOptions['prerenderOptions']; type AppShellOptions = NormalizedApplicationBuildOptions['appShellOptions']; @@ -239,7 +240,7 @@ async function renderPages( ? addLeadingSlash(route.slice(baseHrefPathnameWithLeadingSlash.length)) : route; - const outPath = posix.join(removeLeadingSlash(routeWithoutBaseHref), 'index.html'); + const outPath = stripLeadingSlash(posix.join(routeWithoutBaseHref, 'index.html')); if (typeof redirectTo === 'string') { output[outPath] = { content: generateRedirectStaticPage(redirectTo), appShellRoute: false }; @@ -297,7 +298,7 @@ async function getAllRoutes( let appShellRoute: string | undefined; if (appShellOptions) { - appShellRoute = urlJoin(baseHref, appShellOptions.route); + appShellRoute = joinUrlParts(baseHref, appShellOptions.route); routes.push({ renderMode: RouteRenderMode.Prerender, @@ -310,7 +311,7 @@ async function getAllRoutes( for (const route of routesFromFile) { routes.push({ renderMode: RouteRenderMode.Prerender, - route: urlJoin(baseHref, route.trim()), + route: joinUrlParts(baseHref, route.trim()), }); } } @@ -368,40 +369,3 @@ async function getAllRoutes( void renderWorker.destroy(); } } - -function addLeadingSlash(value: string): string { - return value[0] === '/' ? value : '/' + value; -} - -function addTrailingSlash(url: string): string { - return url[url.length - 1] === '/' ? url : `${url}/`; -} - -function removeLeadingSlash(value: string): string { - return value[0] === '/' ? value.slice(1) : value; -} - -/** - * Generates a static HTML page with a meta refresh tag to redirect the user to a specified URL. - * - * This function creates a simple HTML page that performs a redirect using a meta tag. - * It includes a fallback link in case the meta-refresh doesn't work. - * - * @param url - The URL to which the page should redirect. - * @returns The HTML content of the static redirect page. - */ -function generateRedirectStaticPage(url: string): string { - return ` - - - - - Redirecting - - - -
Redirecting to ${url}
- - -`.trim(); -} diff --git a/packages/angular/build/src/utils/server-rendering/render-worker.ts b/packages/angular/build/src/utils/server-rendering/render-worker.ts index f3fc8e93a0d0..7ded0550b826 100644 --- a/packages/angular/build/src/utils/server-rendering/render-worker.ts +++ b/packages/angular/build/src/utils/server-rendering/render-worker.ts @@ -12,6 +12,7 @@ import type { ESMInMemoryFileLoaderWorkerData } from './esm-in-memory-loader/loa import { patchFetchToLoadInMemoryAssets } from './fetch-patch'; import { DEFAULT_URL, launchServer } from './launch-server'; import { loadEsmModuleFromMemory } from './load-esm-from-memory'; +import { generateRedirectStaticPage } from './utils'; export interface RenderWorkerData extends ESMInMemoryFileLoaderWorkerData { assetFiles: Record; @@ -48,7 +49,13 @@ async function renderPage({ url }: RenderOptions): Promise { new Request(new URL(url, serverURL), { signal: AbortSignal.timeout(30_000) }), ); - return response ? response.text() : null; + if (!response) { + return null; + } + + const location = response.headers.get('Location'); + + return location ? generateRedirectStaticPage(location) : response.text(); } async function initialize() { diff --git a/packages/angular/build/src/utils/server-rendering/utils.ts b/packages/angular/build/src/utils/server-rendering/utils.ts index a322780e391d..c740d4de06c4 100644 --- a/packages/angular/build/src/utils/server-rendering/utils.ts +++ b/packages/angular/build/src/utils/server-rendering/utils.ts @@ -19,3 +19,28 @@ export function isSsrRequestHandler( ): value is ReturnType { return typeof value === 'function' && '__ng_request_handler__' in value; } + +/** + * Generates a static HTML page with a meta refresh tag to redirect the user to a specified URL. + * + * This function creates a simple HTML page that performs a redirect using a meta tag. + * It includes a fallback link in case the meta-refresh doesn't work. + * + * @param url - The URL to which the page should redirect. + * @returns The HTML content of the static redirect page. + */ +export function generateRedirectStaticPage(url: string): string { + return ` + + + + + Redirecting + + + +
Redirecting to ${url}
+ + +`.trim(); +} diff --git a/packages/angular/build/src/utils/url.ts b/packages/angular/build/src/utils/url.ts index d3f1e5791276..689eac37eab5 100644 --- a/packages/angular/build/src/utils/url.ts +++ b/packages/angular/build/src/utils/url.ts @@ -6,11 +6,117 @@ * found in the LICENSE file at https://angular.dev/license */ -export function urlJoin(...parts: string[]): string { - const [p, ...rest] = parts; +/** + * Removes the trailing slash from a URL if it exists. + * + * @param url - The URL string from which to remove the trailing slash. + * @returns The URL string without a trailing slash. + * + * @example + * ```js + * stripTrailingSlash('path/'); // 'path' + * stripTrailingSlash('/path'); // '/path' + * stripTrailingSlash('/'); // '/' + * stripTrailingSlash(''); // '' + * ``` + */ +export function stripTrailingSlash(url: string): string { + // Check if the last character of the URL is a slash + return url.length > 1 && url.at(-1) === '/' ? url.slice(0, -1) : url; +} + +/** + * Removes the leading slash from a URL if it exists. + * + * @param url - The URL string from which to remove the leading slash. + * @returns The URL string without a leading slash. + * + * @example + * ```js + * stripLeadingSlash('/path'); // 'path' + * stripLeadingSlash('/path/'); // 'path/' + * stripLeadingSlash('/'); // '/' + * stripLeadingSlash(''); // '' + * ``` + */ +export function stripLeadingSlash(url: string): string { + // Check if the first character of the URL is a slash + return url.length > 1 && url[0] === '/' ? url.slice(1) : url; +} + +/** + * Adds a leading slash to a URL if it does not already have one. + * + * @param url - The URL string to which the leading slash will be added. + * @returns The URL string with a leading slash. + * + * @example + * ```js + * addLeadingSlash('path'); // '/path' + * addLeadingSlash('/path'); // '/path' + * ``` + */ +export function addLeadingSlash(url: string): string { + // Check if the URL already starts with a slash + return url[0] === '/' ? url : `/${url}`; +} + +/** + * Adds a trailing slash to a URL if it does not already have one. + * + * @param url - The URL string to which the trailing slash will be added. + * @returns The URL string with a trailing slash. + * + * @example + * ```js + * addTrailingSlash('path'); // 'path/' + * addTrailingSlash('path/'); // 'path/' + * ``` + */ +export function addTrailingSlash(url: string): string { + // Check if the URL already end with a slash + return url.at(-1) === '/' ? url : `${url}/`; +} + +/** + * Joins URL parts into a single URL string. + * + * This function takes multiple URL segments, normalizes them by removing leading + * and trailing slashes where appropriate, and then joins them into a single URL. + * + * @param parts - The parts of the URL to join. Each part can be a string with or without slashes. + * @returns The joined URL string, with normalized slashes. + * + * @example + * ```js + * joinUrlParts('path/', '/to/resource'); // '/path/to/resource' + * joinUrlParts('/path/', 'to/resource'); // '/path/to/resource' + * joinUrlParts('http://localhost/path/', 'to/resource'); // 'http://localhost/path/to/resource' + * joinUrlParts('', ''); // '/' + * ``` + */ +export function joinUrlParts(...parts: string[]): string { + const normalizeParts: string[] = []; + for (const part of parts) { + if (part === '') { + // Skip any empty parts + continue; + } + + let normalizedPart = part; + if (part[0] === '/') { + normalizedPart = normalizedPart.slice(1); + } + if (part.at(-1) === '/') { + normalizedPart = normalizedPart.slice(0, -1); + } + if (normalizedPart !== '') { + normalizeParts.push(normalizedPart); + } + } + + const protocolMatch = normalizeParts.length && /^https?:\/\//.test(normalizeParts[0]); + const joinedParts = normalizeParts.join('/'); - // Remove trailing slash from first part - // Join all parts with `/` - // Dedupe double slashes from path names - return p.replace(/\/$/, '') + ('/' + rest.join('/')).replace(/\/\/+/g, '/'); + return protocolMatch ? joinedParts : addLeadingSlash(joinedParts); } diff --git a/packages/angular/cli/BUILD.bazel b/packages/angular/cli/BUILD.bazel index 6cbb09b36c31..5158ce1dc26a 100644 --- a/packages/angular/cli/BUILD.bazel +++ b/packages/angular/cli/BUILD.bazel @@ -15,6 +15,17 @@ package(default_visibility = ["//visibility:public"]) npm_link_all_packages() +genrule( + name = "angular_best_practices", + srcs = [ + "//:node_modules/@angular/core/dir", + ], + outs = ["src/commands/mcp/resources/best-practices.md"], + cmd = """ + cp "$(location //:node_modules/@angular/core/dir)/resources/best-practices.md" $@ + """, +) + RUNTIME_ASSETS = glob( include = [ "bin/**/*", @@ -27,6 +38,7 @@ RUNTIME_ASSETS = glob( ) + [ "//packages/angular/cli:lib/config/schema.json", "//packages/angular/cli:lib/code-examples.db", + ":angular_best_practices", ] ts_project( diff --git a/packages/angular/cli/src/command-builder/architect-base-command-module.ts b/packages/angular/cli/src/command-builder/architect-base-command-module.ts index 566e0e62b209..fb3508777d74 100644 --- a/packages/angular/cli/src/command-builder/architect-base-command-module.ts +++ b/packages/angular/cli/src/command-builder/architect-base-command-module.ts @@ -12,8 +12,7 @@ import { WorkspaceNodeModulesArchitectHost, } from '@angular-devkit/architect/node'; import { json } from '@angular-devkit/core'; -import { existsSync } from 'node:fs'; -import { resolve } from 'node:path'; +import { createRequire } from 'node:module'; import { isPackageNameSafeForAnalytics } from '../analytics/analytics'; import { EventCustomDimension, EventCustomMetric } from '../analytics/analytics-parameters'; import { assertIsError } from '../utilities/error'; @@ -210,15 +209,13 @@ export abstract class ArchitectBaseCommandModule return; } - // Check if yarn PnP is used. https://yarnpkg.com/advanced/pnpapi#processversionspnp - if (process.versions.pnp) { - return; - } + const workspaceResolve = createRequire(basePath + '/').resolve; + + try { + workspaceResolve('@angular/core'); - // Check for a `node_modules` directory (npm, yarn non-PnP, etc.) - if (existsSync(resolve(basePath, 'node_modules'))) { return; - } + } catch {} this.context.logger.warn( `Node packages may not be installed. Try installing with '${this.context.packageManager.name} install'.`, diff --git a/packages/angular/cli/src/command-builder/architect-command-module.ts b/packages/angular/cli/src/command-builder/architect-command-module.ts index 4218c6274521..98e270cf1dad 100644 --- a/packages/angular/cli/src/command-builder/architect-command-module.ts +++ b/packages/angular/cli/src/command-builder/architect-command-module.ts @@ -41,7 +41,8 @@ export abstract class ArchitectCommandModule // Add default builder if target is not in project and a command default is provided if (this.findDefaultBuilderName && this.context.workspace) { for (const [project, projectDefinition] of this.context.workspace.projects) { - if (projectDefinition.targets.has(target)) { + const targetDefinition = projectDefinition.targets.get(target); + if (targetDefinition?.builder) { continue; } @@ -49,7 +50,13 @@ export abstract class ArchitectCommandModule project, target, }); - if (defaultBuilder) { + if (!defaultBuilder) { + continue; + } + + if (targetDefinition) { + targetDefinition.builder = defaultBuilder; + } else { projectDefinition.targets.set(target, { builder: defaultBuilder, }); diff --git a/packages/angular/cli/src/command-builder/command-module.ts b/packages/angular/cli/src/command-builder/command-module.ts index 64f7ac7377c7..d036656cf2dd 100644 --- a/packages/angular/cli/src/command-builder/command-module.ts +++ b/packages/angular/cli/src/command-builder/command-module.ts @@ -14,8 +14,7 @@ import type { Argv, CamelCaseKey, CommandModule as YargsCommandModule, - // Resolution mode is required due to CamelCaseKey missing from esm types -} from 'yargs' with { 'resolution-mode': 'require' }; +} from 'yargs'; import { Parser as yargsParser } from 'yargs/helpers'; import { getAnalyticsUserId } from '../analytics/analytics'; import { AnalyticsCollector } from '../analytics/analytics-collector'; diff --git a/packages/angular/cli/src/command-builder/command-runner.ts b/packages/angular/cli/src/command-builder/command-runner.ts index 8843ea09d461..cb4ab2c8467e 100644 --- a/packages/angular/cli/src/command-builder/command-runner.ts +++ b/packages/angular/cli/src/command-builder/command-runner.ts @@ -8,7 +8,7 @@ import { logging } from '@angular-devkit/core'; import yargs from 'yargs'; -import { Parser } from 'yargs/helpers'; +import { Parser as yargsParser } from 'yargs/helpers'; import { CommandConfig, CommandNames, @@ -29,8 +29,6 @@ import { import { jsonHelpUsage } from './utilities/json-help'; import { createNormalizeOptionsMiddleware } from './utilities/normalize-options-middleware'; -const yargsParser = Parser as unknown as typeof Parser.default; - export async function runCommand(args: string[], logger: logging.Logger): Promise { const { $0, diff --git a/packages/angular/cli/src/command-builder/utilities/json-schema.ts b/packages/angular/cli/src/command-builder/utilities/json-schema.ts index e9fbd7e0e27a..0a4215be8eed 100644 --- a/packages/angular/cli/src/command-builder/utilities/json-schema.ts +++ b/packages/angular/cli/src/command-builder/utilities/json-schema.ts @@ -334,7 +334,7 @@ export async function parseJsonSchemaToOptions( // Skip any non-property items. return; } - const name = ptr[ptr.length - 1]; + const name = ptr.at(-1) as string; const types = getSupportedTypes(current); diff --git a/packages/angular/cli/src/commands/add/cli.ts b/packages/angular/cli/src/commands/add/cli.ts index 1ea2fff699c6..aec0014b9114 100644 --- a/packages/angular/cli/src/commands/add/cli.ts +++ b/packages/angular/cli/src/commands/add/cli.ts @@ -493,7 +493,9 @@ export default class AddCommandModule // Only show if installation will actually occur task.title = 'Installing package'; - if (context.savePackage === false) { + if (context.savePackage === false && packageManager.name !== PackageManager.Bun) { + // Bun has a `--no-save` option which we are using to + // install the package and not update the package.json and the lock file. task.title += ' in temporary location'; // Temporary packages are located in a different directory diff --git a/packages/angular/cli/src/commands/mcp/host.ts b/packages/angular/cli/src/commands/mcp/host.ts new file mode 100644 index 000000000000..ad57b03550bf --- /dev/null +++ b/packages/angular/cli/src/commands/mcp/host.ts @@ -0,0 +1,130 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +/** + * @fileoverview + * This file defines an abstraction layer for operating-system or file-system operations, such as + * command execution. This allows for easier testing by enabling the injection of mock or + * test-specific implementations. + */ + +import { existsSync as nodeExistsSync } from 'fs'; +import { spawn } from 'node:child_process'; +import { Stats } from 'node:fs'; +import { stat } from 'node:fs/promises'; + +/** + * An error thrown when a command fails to execute. + */ +export class CommandError extends Error { + constructor( + message: string, + public readonly stdout: string, + public readonly stderr: string, + public readonly code: number | null, + ) { + super(message); + } +} + +/** + * An abstraction layer for operating-system or file-system operations. + */ +export interface Host { + /** + * Gets the stats of a file or directory. + * @param path The path to the file or directory. + * @returns A promise that resolves to the stats. + */ + stat(path: string): Promise; + + /** + * Checks if a path exists on the file system. + * @param path The path to check. + * @returns A boolean indicating whether the path exists. + */ + existsSync(path: string): boolean; + + /** + * Spawns a child process and returns a promise that resolves with the process's + * output or rejects with a structured error. + * @param command The command to run. + * @param args The arguments to pass to the command. + * @param options Options for the child process. + * @returns A promise that resolves with the standard output and standard error of the command. + */ + runCommand( + command: string, + args: readonly string[], + options?: { + timeout?: number; + stdio?: 'pipe' | 'ignore'; + cwd?: string; + env?: Record; + }, + ): Promise<{ stdout: string; stderr: string }>; +} + +/** + * A concrete implementation of the `Host` interface that runs on a local workspace. + */ +export const LocalWorkspaceHost: Host = { + stat, + existsSync: nodeExistsSync, + runCommand: async ( + command: string, + args: readonly string[], + options: { + timeout?: number; + stdio?: 'pipe' | 'ignore'; + cwd?: string; + env?: Record; + } = {}, + ): Promise<{ stdout: string; stderr: string }> => { + const signal = options.timeout ? AbortSignal.timeout(options.timeout) : undefined; + + return new Promise((resolve, reject) => { + const childProcess = spawn(command, args, { + shell: false, + stdio: options.stdio ?? 'pipe', + signal, + cwd: options.cwd, + env: { + ...process.env, + ...options.env, + }, + }); + + let stdout = ''; + childProcess.stdout?.on('data', (data) => (stdout += data.toString())); + + let stderr = ''; + childProcess.stderr?.on('data', (data) => (stderr += data.toString())); + + childProcess.on('close', (code) => { + if (code === 0) { + resolve({ stdout, stderr }); + } else { + const message = `Process exited with code ${code}.`; + reject(new CommandError(message, stdout, stderr, code)); + } + }); + + childProcess.on('error', (err) => { + if (err.name === 'AbortError') { + const message = `Process timed out.`; + reject(new CommandError(message, stdout, stderr, null)); + + return; + } + const message = `Process failed with error: ${err.message}`; + reject(new CommandError(message, stdout, stderr, null)); + }); + }); + }, +}; diff --git a/packages/angular/cli/src/commands/mcp/mcp-server.ts b/packages/angular/cli/src/commands/mcp/mcp-server.ts index 9f84bcf55704..8a006c98be60 100644 --- a/packages/angular/cli/src/commands/mcp/mcp-server.ts +++ b/packages/angular/cli/src/commands/mcp/mcp-server.ts @@ -30,13 +30,14 @@ const STABLE_TOOLS = [ DOC_SEARCH_TOOL, FIND_EXAMPLE_TOOL, LIST_PROJECTS_TOOL, + ZONELESS_MIGRATION_TOOL, ] as const; /** * The set of tools that are available but not enabled by default. * These tools are considered experimental and may have limitations. */ -export const EXPERIMENTAL_TOOLS = [MODERNIZE_TOOL, ZONELESS_MIGRATION_TOOL] as const; +export const EXPERIMENTAL_TOOLS = [MODERNIZE_TOOL] as const; export async function createMcpServer( options: { diff --git a/packages/angular/cli/src/commands/mcp/resources/best-practices.md b/packages/angular/cli/src/commands/mcp/resources/best-practices.md deleted file mode 100644 index e3b246935258..000000000000 --- a/packages/angular/cli/src/commands/mcp/resources/best-practices.md +++ /dev/null @@ -1,47 +0,0 @@ -You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices. - -## TypeScript Best Practices - -- Use strict type checking -- Prefer type inference when the type is obvious -- Avoid the `any` type; use `unknown` when type is uncertain - -## Angular Best Practices - -- Always use standalone components over NgModules -- Must NOT set `standalone: true` inside Angular decorators. It's the default. -- Use signals for state management -- Implement lazy loading for feature routes -- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead -- Use `NgOptimizedImage` for all static images. - - `NgOptimizedImage` does not work for inline base64 images. - -## Components - -- Keep components small and focused on a single responsibility -- Use `input()` and `output()` functions instead of decorators -- Use `computed()` for derived state -- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator -- Prefer inline templates for small components -- Prefer Reactive forms instead of Template-driven ones -- Do NOT use `ngClass`, use `class` bindings instead -- DO NOT use `ngStyle`, use `style` bindings instead - -## State Management - -- Use signals for local component state -- Use `computed()` for derived state -- Keep state transformations pure and predictable -- Do NOT use `mutate` on signals, use `update` or `set` instead - -## Templates - -- Keep templates simple and avoid complex logic -- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch` -- Use the async pipe to handle observables - -## Services - -- Design services around a single responsibility -- Use the `providedIn: 'root'` option for singleton services -- Use the `inject()` function instead of constructor injection diff --git a/packages/angular/cli/src/commands/mcp/tools/examples.ts b/packages/angular/cli/src/commands/mcp/tools/examples.ts index 8866761017a6..023f2713e24c 100644 --- a/packages/angular/cli/src/commands/mcp/tools/examples.ts +++ b/packages/angular/cli/src/commands/mcp/tools/examples.ts @@ -20,8 +20,8 @@ const findExampleInputSchema = z.object({ .describe( 'The absolute path to the `angular.json` file for the workspace. This is used to find the ' + 'version-specific code examples that correspond to the installed version of the ' + - 'Angular framework. You **MUST** get this path from the `list_projects` tool. If omitted, ' + - 'the tool will search the generic code examples bundled with the CLI.', + 'Angular framework. You **MUST** get this path from the `list_projects` tool. ' + + 'If omitted, the tool will search the generic code examples bundled with the CLI.', ), query: z .string() @@ -200,9 +200,15 @@ new or evolving features. }); /** - * Attempts to find a version-specific example database from the user's installed - * version of `@angular/core`. It looks for a custom `angular` metadata property in the - * framework's `package.json` to locate the database. + * A list of known Angular packages that may contain example databases. + * The tool will attempt to resolve and load example databases from these packages. + */ +const KNOWN_EXAMPLE_PACKAGES = ['@angular/core', '@angular/aria', '@angular/forms']; + +/** + * Attempts to find version-specific example databases from the user's installed + * versions of known Angular packages. It looks for a custom `angular` metadata property in each + * package's `package.json` to locate the database. * * @example A sample `package.json` `angular` field: * ```json @@ -218,79 +224,74 @@ new or evolving features. * * @param workspacePath The absolute path to the user's `angular.json` file. * @param logger The MCP tool context logger for reporting warnings. - * @returns A promise that resolves to an object containing the database path and source, - * or `undefined` if the database could not be resolved. + * @returns A promise that resolves to an array of objects, each containing a database path and source. */ -async function getVersionSpecificExampleDatabase( +async function getVersionSpecificExampleDatabases( workspacePath: string, logger: McpToolContext['logger'], -): Promise<{ dbPath: string; source: string } | undefined> { - // 1. Resolve the path to package.json - let pkgJsonPath: string; - try { - const workspaceRequire = createRequire(workspacePath); - pkgJsonPath = workspaceRequire.resolve('@angular/core/package.json'); - } catch (e) { - logger.warn( - `Could not resolve '@angular/core/package.json' from '${workspacePath}'. ` + - 'Is Angular installed in this project? Falling back to the bundled examples.', - ); - - return undefined; - } +): Promise<{ dbPath: string; source: string }[]> { + const workspaceRequire = createRequire(workspacePath); + const databases: { dbPath: string; source: string }[] = []; + + for (const packageName of KNOWN_EXAMPLE_PACKAGES) { + // 1. Resolve the path to package.json + let pkgJsonPath: string; + try { + pkgJsonPath = workspaceRequire.resolve(`${packageName}/package.json`); + } catch (e) { + // This is not a warning because the user may not have all known packages installed. + continue; + } - // 2. Read and parse package.json, then find the database. - try { - const pkgJsonContent = await readFile(pkgJsonPath, 'utf-8'); - const pkgJson = JSON.parse(pkgJsonContent); - const examplesInfo = pkgJson['angular']?.examples; - - if (examplesInfo && examplesInfo.format === 'sqlite' && typeof examplesInfo.path === 'string') { - const packageDirectory = path.dirname(pkgJsonPath); - const dbPath = path.resolve(packageDirectory, examplesInfo.path); - - // Ensure the resolved database path is within the package boundary. - const relativePath = path.relative(packageDirectory, dbPath); - if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { - logger.warn( - `Detected a potential path traversal attempt in '${pkgJsonPath}'. ` + - `The path '${examplesInfo.path}' escapes the package boundary. ` + - 'Falling back to the bundled examples.', - ); - - return undefined; - } + // 2. Read and parse package.json, then find the database. + try { + const pkgJsonContent = await readFile(pkgJsonPath, 'utf-8'); + const pkgJson = JSON.parse(pkgJsonContent); + const examplesInfo = pkgJson['angular']?.examples; + + if ( + examplesInfo && + examplesInfo.format === 'sqlite' && + typeof examplesInfo.path === 'string' + ) { + const packageDirectory = path.dirname(pkgJsonPath); + const dbPath = path.resolve(packageDirectory, examplesInfo.path); + + // Ensure the resolved database path is within the package boundary. + const relativePath = path.relative(packageDirectory, dbPath); + if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) { + logger.warn( + `Detected a potential path traversal attempt in '${pkgJsonPath}'. ` + + `The path '${examplesInfo.path}' escapes the package boundary. ` + + 'This database will be skipped.', + ); + continue; + } - // Check the file size to prevent reading a very large file. - const stats = await stat(dbPath); - if (stats.size > 10 * 1024 * 1024) { - // 10MB - logger.warn( - `The example database at '${dbPath}' is larger than 10MB (${stats.size} bytes). ` + - 'This is unexpected and the file will not be used. Falling back to the bundled examples.', - ); + // Check the file size to prevent reading a very large file. + const stats = await stat(dbPath); + if (stats.size > 10 * 1024 * 1024) { + // 10MB + logger.warn( + `The example database at '${dbPath}' is larger than 10MB (${stats.size} bytes). ` + + 'This is unexpected and the file will not be used.', + ); + continue; + } - return undefined; + const source = `package ${packageName}@${pkgJson.version}`; + databases.push({ dbPath, source }); } - - const source = `framework version ${pkgJson.version}`; - - return { dbPath, source }; - } else { + } catch (e) { logger.warn( - `Did not find valid 'angular.examples' metadata in '${pkgJsonPath}'. ` + - 'Falling back to the bundled examples.', + `Failed to read or parse version-specific examples metadata referenced in '${pkgJsonPath}': ${ + e instanceof Error ? e.message : e + }.`, ); } - } catch (e) { - logger.warn( - `Failed to read or parse version-specific examples metadata referenced in '${pkgJsonPath}': ${ - e instanceof Error ? e.message : e - }. Falling back to the bundled examples.`, - ); } - return undefined; + return databases; } async function createFindExampleHandler({ logger, exampleDatabasePath }: McpToolContext) { @@ -303,37 +304,57 @@ async function createFindExampleHandler({ logger, exampleDatabasePath }: McpTool return async (input: FindExampleInput) => { // If the dev-time override is present, use it and bypass all other logic. if (runtimeDb) { - return queryDatabase(runtimeDb, input); + return queryDatabase([runtimeDb], input); } - let dbPath: string | undefined; + const resolvedDbs: { path: string; source: string }[] = []; - // First, try to get the version-specific guide. + // First, try to get all available version-specific guides. if (input.workspacePath) { - const versionSpecific = await getVersionSpecificExampleDatabase(input.workspacePath, logger); - if (versionSpecific) { - dbPath = versionSpecific.dbPath; + const versionSpecificDbs = await getVersionSpecificExampleDatabases( + input.workspacePath, + logger, + ); + for (const db of versionSpecificDbs) { + resolvedDbs.push({ path: db.dbPath, source: db.source }); } } - // If the version-specific guide was not found for any reason, fall back to the bundled version. - if (!dbPath) { - dbPath = exampleDatabasePath; + // If no version-specific guides were found for any reason, fall back to the bundled version. + if (resolvedDbs.length === 0 && exampleDatabasePath) { + resolvedDbs.push({ path: exampleDatabasePath, source: 'bundled' }); } - if (!dbPath) { + if (resolvedDbs.length === 0) { // This should be prevented by the registration logic in mcp-server.ts - throw new Error('Example database path is not available.'); + throw new Error('No example databases are available.'); } const { DatabaseSync } = await import('node:sqlite'); - const db = new DatabaseSync(dbPath, { readOnly: true }); + const dbConnections: DatabaseSync[] = []; + + for (const { path, source } of resolvedDbs) { + const db = new DatabaseSync(path, { readOnly: true }); + try { + validateDatabaseSchema(db, source); + dbConnections.push(db); + } catch (e) { + logger.warn((e as Error).message); + // If a database is invalid, we should not query it, but we should not fail the whole tool. + // We will just skip this database and try to use the others. + continue; + } + } - return queryDatabase(db, input); + if (dbConnections.length === 0) { + throw new Error('All available example databases were invalid. Cannot perform query.'); + } + + return queryDatabase(dbConnections, input); }; } -function queryDatabase(db: DatabaseSync, input: FindExampleInput) { +function queryDatabase(dbs: DatabaseSync[], input: FindExampleInput) { const { query, keywords, required_packages, related_concepts, includeExperimental } = input; // Build the query dynamically @@ -342,7 +363,12 @@ function queryDatabase(db: DatabaseSync, input: FindExampleInput) { `SELECT e.title, e.summary, e.keywords, e.required_packages, e.related_concepts, e.related_tools, e.content, ` + // The `snippet` function generates a contextual snippet of the matched text. // Column 6 is the `content` column. We highlight matches with asterisks and limit the snippet size. - "snippet(examples_fts, 6, '**', '**', '...', 15) AS snippet " + + "snippet(examples_fts, 6, '**', '**', '...', 15) AS snippet, " + + // The `bm25` function returns the relevance score of the match. The weights + // assigned to each column boost the ranking of documents where the search + // term appears in a more important field. + // Column order: title, summary, keywords, required_packages, related_concepts, related_tools, content + 'bm25(examples_fts, 10.0, 5.0, 5.0, 1.0, 2.0, 1.0, 1.0) AS rank ' + 'FROM examples e JOIN examples_fts ON e.id = examples_fts.rowid'; const whereClauses = []; @@ -374,31 +400,38 @@ function queryDatabase(db: DatabaseSync, input: FindExampleInput) { sql += ` WHERE ${whereClauses.join(' AND ')}`; } - // Order the results by relevance using the BM25 algorithm. - // The weights assigned to each column boost the ranking of documents where the - // search term appears in a more important field. - // Column order: title, summary, keywords, required_packages, related_concepts, related_tools, content - sql += ' ORDER BY bm25(examples_fts, 10.0, 5.0, 5.0, 1.0, 2.0, 1.0, 1.0);'; - - const queryStatement = db.prepare(sql); - // Query database and return results const examples = []; const textContent = []; - for (const exampleRecord of queryStatement.all(...params)) { - const record = exampleRecord as Record; - const example = { - title: record['title'], - summary: record['summary'], - keywords: JSON.parse(record['keywords'] || '[]') as string[], - required_packages: JSON.parse(record['required_packages'] || '[]') as string[], - related_concepts: JSON.parse(record['related_concepts'] || '[]') as string[], - related_tools: JSON.parse(record['related_tools'] || '[]') as string[], - content: record['content'], - snippet: record['snippet'], - }; - examples.push(example); + for (const db of dbs) { + const queryStatement = db.prepare(sql); + for (const exampleRecord of queryStatement.all(...params)) { + const record = exampleRecord as Record; + const example = { + title: record['title'] as string, + summary: record['summary'] as string, + keywords: JSON.parse((record['keywords'] as string) || '[]') as string[], + required_packages: JSON.parse((record['required_packages'] as string) || '[]') as string[], + related_concepts: JSON.parse((record['related_concepts'] as string) || '[]') as string[], + related_tools: JSON.parse((record['related_tools'] as string) || '[]') as string[], + content: record['content'] as string, + snippet: record['snippet'] as string, + rank: record['rank'] as number, + }; + examples.push(example); + } + } + + // Order the combined results by relevance. + // The `bm25` algorithm returns a smaller number for a more relevant match. + examples.sort((a, b) => a.rank - b.rank); + + // The `rank` field is an internal implementation detail for sorting and should not be + // returned to the user. We create a new array of examples without the `rank`. + const finalExamples = examples.map(({ rank, ...rest }) => rest); + + for (const example of finalExamples) { // Also create a more structured text output let text = `## Example: ${example.title}\n**Summary:** ${example.summary}`; if (example.snippet) { @@ -410,7 +443,7 @@ function queryDatabase(db: DatabaseSync, input: FindExampleInput) { return { content: textContent, - structuredContent: { examples }, + structuredContent: { examples: finalExamples }, }; } @@ -571,6 +604,19 @@ async function setupRuntimeExamples(examplesPath: string): Promise const db = new DatabaseSync(':memory:'); // Create a relational table to store the structured example data. + db.exec(` + CREATE TABLE metadata ( + key TEXT PRIMARY KEY NOT NULL, + value TEXT NOT NULL + ); + `); + + db.exec(` + INSERT INTO metadata (key, value) VALUES + ('schema_version', '1'), + ('created_at', '${new Date().toISOString()}'); + `); + db.exec(` CREATE TABLE examples ( id INTEGER PRIMARY KEY, @@ -669,3 +715,41 @@ async function setupRuntimeExamples(examplesPath: string): Promise return db; } + +const EXPECTED_SCHEMA_VERSION = 1; + +/** + * Validates the schema version of the example database. + * + * @param db The database connection to validate. + * @param dbSource A string identifying the source of the database (e.g., 'bundled' or a version number). + * @throws An error if the schema version is missing or incompatible. + */ +function validateDatabaseSchema(db: DatabaseSync, dbSource: string): void { + const schemaVersionResult = db + .prepare('SELECT value FROM metadata WHERE key = ?') + .get('schema_version') as { value: string } | undefined; + const actualSchemaVersion = schemaVersionResult ? Number(schemaVersionResult.value) : undefined; + + if (actualSchemaVersion !== EXPECTED_SCHEMA_VERSION) { + db.close(); + + let errorMessage: string; + if (actualSchemaVersion === undefined) { + errorMessage = 'The example database is missing a schema version and cannot be used.'; + } else if (actualSchemaVersion > EXPECTED_SCHEMA_VERSION) { + errorMessage = + `This project's example database (version ${actualSchemaVersion})` + + ` is newer than what this version of the Angular CLI supports (version ${EXPECTED_SCHEMA_VERSION}).` + + ' Please update your `@angular/cli` package to a newer version.'; + } else { + errorMessage = + `This version of the Angular CLI (expects schema version ${EXPECTED_SCHEMA_VERSION})` + + ` requires a newer example database than the one found in this project (version ${actualSchemaVersion}).`; + } + + throw new Error( + `Incompatible example database schema from source '${dbSource}':\n${errorMessage}`, + ); + } +} diff --git a/packages/angular/cli/src/commands/mcp/tools/modernize.ts b/packages/angular/cli/src/commands/mcp/tools/modernize.ts index 58851ca3df09..2ad3e737578c 100644 --- a/packages/angular/cli/src/commands/mcp/tools/modernize.ts +++ b/packages/angular/cli/src/commands/mcp/tools/modernize.ts @@ -6,8 +6,10 @@ * found in the LICENSE file at https://angular.dev/license */ +import { dirname, join, relative } from 'path'; import { z } from 'zod'; -import { declareTool } from './tool-registry'; +import { CommandError, Host, LocalWorkspaceHost } from '../host'; +import { McpToolDeclaration, declareTool } from './tool-registry'; interface Transformation { name: string; @@ -18,13 +20,13 @@ interface Transformation { const TRANSFORMATIONS: Array = [ { - name: 'control-flow-migration', + name: 'control-flow', description: 'Migrates from `*ngIf`, `*ngFor`, and `*ngSwitch` to the new `@if`, `@for`, and `@switch` block syntax in templates.', documentationUrl: 'https://angular.dev/reference/migrations/control-flow', }, { - name: 'self-closing-tags-migration', + name: 'self-closing-tag', description: 'Converts tags for elements with no content to be self-closing (e.g., `` becomes ``).', documentationUrl: 'https://angular.dev/reference/migrations/self-closing-tags', @@ -67,57 +69,143 @@ const TRANSFORMATIONS: Array = [ ]; const modernizeInputSchema = z.object({ - // Casting to [string, ...string[]] since the enum definition requires a nonempty array. + directories: z + .array(z.string()) + .optional() + .describe('A list of paths to directories with files to modernize.'), transformations: z .array(z.enum(TRANSFORMATIONS.map((t) => t.name) as [string, ...string[]])) .optional() + .describe('A list of specific transformations to apply.'), +}); + +const modernizeOutputSchema = z.object({ + instructions: z + .array(z.string()) + .optional() .describe( - 'A list of specific transformations to get instructions for. ' + - 'If omitted, general guidance is provided.', + 'Migration summary, as well as any instructions that need to be performed to complete the migrations.', ), + stdout: z.string().optional().describe('The stdout from the executed commands.'), + stderr: z.string().optional().describe('The stderr from the executed commands.'), }); export type ModernizeInput = z.infer; +export type ModernizeOutput = z.infer; + +function createToolOutput(structuredContent: ModernizeOutput) { + return { + content: [{ type: 'text' as const, text: JSON.stringify(structuredContent, null, 2) }], + structuredContent, + }; +} + +function findAngularJsonDir(startDir: string, host: Host): string | null { + let currentDir = startDir; + while (true) { + if (host.existsSync(join(currentDir, 'angular.json'))) { + return currentDir; + } + const parentDir = dirname(currentDir); + if (parentDir === currentDir) { + return null; + } + currentDir = parentDir; + } +} + +export async function runModernization(input: ModernizeInput, host: Host) { + const transformationNames = input.transformations ?? []; + const directories = input.directories ?? []; -function generateInstructions(transformationNames: string[]): string[] { if (transformationNames.length === 0) { - return [ - 'See https://angular.dev/best-practices for Angular best practices. ' + - 'You can call this tool if you have specific transformation you want to run.', - ]; + return createToolOutput({ + instructions: [ + 'See https://angular.dev/best-practices for Angular best practices. ' + + 'You can call this tool if you have specific transformation you want to run.', + ], + }); + } + if (directories.length === 0) { + return createToolOutput({ + instructions: [ + 'Provide this tool with a list of directory paths in your workspace ' + + 'to run the modernization on.', + ], + }); + } + + const firstDir = directories[0]; + const executionDir = (await host.stat(firstDir)).isDirectory() ? firstDir : dirname(firstDir); + + const angularProjectRoot = findAngularJsonDir(executionDir, host); + if (!angularProjectRoot) { + return createToolOutput({ + instructions: ['Could not find an angular.json file in the current or parent directories.'], + }); } const instructions: string[] = []; - const transformationsToRun = TRANSFORMATIONS.filter((t) => transformationNames?.includes(t.name)); + const stdoutMessages: string[] = []; + const stderrMessages: string[] = []; + const transformationsToRun = TRANSFORMATIONS.filter((t) => transformationNames.includes(t.name)); for (const transformation of transformationsToRun) { - let transformationInstructions = ''; if (transformation.instructions) { - transformationInstructions = transformation.instructions; + // This is a complex case, return instructions. + let transformationInstructions = transformation.instructions; + if (transformation.documentationUrl) { + transformationInstructions += `\nFor more information, see ${transformation.documentationUrl}.`; + } + instructions.push(transformationInstructions); } else { - // If no instructions are included, default to running a cli schematic with the transformation name. - const command = `ng generate @angular/core:${transformation.name}`; - transformationInstructions = `To run the ${transformation.name} migration, execute the following command: \`${command}\`.`; - } - if (transformation.documentationUrl) { - transformationInstructions += `\nFor more information, see ${transformation.documentationUrl}.`; + // Simple case, run the command. + for (const dir of directories) { + const relativePath = relative(angularProjectRoot, dir) || '.'; + const command = 'ng'; + const args = ['generate', `@angular/core:${transformation.name}`, '--path', relativePath]; + try { + const { stdout, stderr } = await host.runCommand(command, args, { + cwd: angularProjectRoot, + }); + if (stdout) { + stdoutMessages.push(stdout); + } + if (stderr) { + stderrMessages.push(stderr); + } + instructions.push( + `Migration ${transformation.name} on directory ${relativePath} completed successfully.`, + ); + } catch (e) { + if (e instanceof CommandError) { + if (e.stdout) { + stdoutMessages.push(e.stdout); + } + if (e.stderr) { + stderrMessages.push(e.stderr); + } + } + stderrMessages.push((e as Error).message); + instructions.push( + `Migration ${transformation.name} on directory ${relativePath} failed.`, + ); + } + } } - instructions.push(transformationInstructions); } - return instructions; + return createToolOutput({ + instructions: instructions.length > 0 ? instructions : undefined, + stdout: stdoutMessages?.join('\n\n') || undefined, + stderr: stderrMessages?.join('\n\n') || undefined, + }); } -export async function runModernization(input: ModernizeInput) { - const structuredContent = { instructions: generateInstructions(input.transformations ?? []) }; - - return { - content: [{ type: 'text' as const, text: JSON.stringify(structuredContent) }], - structuredContent, - }; -} - -export const MODERNIZE_TOOL = declareTool({ +export const MODERNIZE_TOOL: McpToolDeclaration< + typeof modernizeInputSchema.shape, + typeof modernizeOutputSchema.shape +> = declareTool({ name: 'modernize', title: 'Modernize Angular Code', description: ` @@ -135,25 +223,19 @@ generating the exact steps needed to perform specific migrations. general best practices guide. -* **Execution:** This tool **provides instructions**, which you **MUST** then execute as shell commands. - It does not modify code directly. +* **Execution:** This tool executes 'ng generate' commands for simple migrations in a temporary + environment using the provided file content. For complex migrations like 'standalone', it + provides instructions which you **MUST** then execute as shell commands. +* **File Modifications:** This tool has been fixed and now correctly finds the node_modules directory in a Bazel environment. * **Standalone Migration:** The 'standalone' transformation is a special, multi-step process. - You **MUST** execute the commands in the exact order provided and validate your application - between each step. + The tool will provide instructions. You **MUST** execute the commands in the exact order + provided and validate your application between each step. * **Transformation List:** The following transformations are available: ${TRANSFORMATIONS.map((t) => ` * ${t.name}: ${t.description}`).join('\n')} `, inputSchema: modernizeInputSchema.shape, - outputSchema: { - instructions: z - .array(z.string()) - .optional() - .describe( - 'A list of instructions and shell commands to run the requested modernizations. ' + - 'Each string in the array is a separate step or command.', - ), - }, + outputSchema: modernizeOutputSchema.shape, isLocalOnly: true, - isReadOnly: true, - factory: () => (input) => runModernization(input), + isReadOnly: false, + factory: () => (input) => runModernization(input, LocalWorkspaceHost), }); diff --git a/packages/angular/cli/src/commands/mcp/tools/modernize_spec.ts b/packages/angular/cli/src/commands/mcp/tools/modernize_spec.ts index 4c5e4cdacdcc..a00894dde5f6 100644 --- a/packages/angular/cli/src/commands/mcp/tools/modernize_spec.ts +++ b/packages/angular/cli/src/commands/mcp/tools/modernize_spec.ts @@ -6,68 +6,201 @@ * found in the LICENSE file at https://angular.dev/license */ -import { ModernizeInput, runModernization } from './modernize'; +import { Stats } from 'fs'; +import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises'; +import { tmpdir } from 'os'; +import { join } from 'path'; +import * as host from '../host'; +import { ModernizeOutput, runModernization } from './modernize'; describe('Modernize Tool', () => { - async function getInstructions(input: ModernizeInput): Promise { - const { structuredContent } = await runModernization(input); + let projectDir: string; + let mockHost: host.Host; - if (!structuredContent || !('instructions' in structuredContent)) { - fail('Expected instructions to be present in the result'); + beforeEach(async () => { + // Create a temporary directory and a fake angular.json to satisfy the tool's project root search. + projectDir = await mkdtemp(join(tmpdir(), 'angular-modernize-test-')); + await writeFile(join(projectDir, 'angular.json'), JSON.stringify({ version: 1, projects: {} })); - return; - } + mockHost = { + runCommand: jasmine.createSpy('runCommand').and.resolveTo({ stdout: '', stderr: '' }), + stat: jasmine.createSpy('stat').and.resolveTo({ isDirectory: () => true } as Stats), + existsSync: jasmine.createSpy('existsSync').and.callFake((p: string) => { + return p === join(projectDir, 'angular.json'); + }), + }; + }); + + afterEach(async () => { + await rm(projectDir, { recursive: true, force: true }); + }); + + it('should return instructions if no transformations are provided', async () => { + const { structuredContent } = (await runModernization({}, mockHost)) as { + structuredContent: ModernizeOutput; + }; + + expect(mockHost.runCommand).not.toHaveBeenCalled(); + expect(structuredContent?.instructions).toEqual([ + 'See https://angular.dev/best-practices for Angular best practices. ' + + 'You can call this tool if you have specific transformation you want to run.', + ]); + }); - return structuredContent.instructions; - } + it('should return instructions if no directories are provided', async () => { + const { structuredContent } = (await runModernization( + { + transformations: ['control-flow'], + }, + mockHost, + )) as { + structuredContent: ModernizeOutput; + }; - it('should return an instruction for a single transformation', async () => { - const instructions = await getInstructions({ - transformations: ['self-closing-tags-migration'], - }); + expect(mockHost.runCommand).not.toHaveBeenCalled(); + expect(structuredContent?.instructions).toEqual([ + 'Provide this tool with a list of directory paths in your workspace ' + + 'to run the modernization on.', + ]); + }); - expect(instructions).toEqual([ - 'To run the self-closing-tags-migration migration, execute the following command: ' + - '`ng generate @angular/core:self-closing-tags-migration`.\nFor more information, ' + - 'see https://angular.dev/reference/migrations/self-closing-tags.', + it('can run a single transformation', async () => { + const { structuredContent } = (await runModernization( + { + directories: [projectDir], + transformations: ['self-closing-tag'], + }, + mockHost, + )) as { structuredContent: ModernizeOutput }; + + expect(mockHost.runCommand).toHaveBeenCalledOnceWith( + 'ng', + ['generate', '@angular/core:self-closing-tag', '--path', '.'], + { cwd: projectDir }, + ); + expect(structuredContent?.instructions).toEqual([ + 'Migration self-closing-tag on directory . completed successfully.', ]); }); - it('should return instructions for multiple transformations', async () => { - const instructions = await getInstructions({ - transformations: ['self-closing-tags-migration', 'inject'], - }); - - const expectedInstructions = [ - 'To run the self-closing-tags-migration migration, execute the following command: ' + - '`ng generate @angular/core:self-closing-tags-migration`.\nFor more information, ' + - 'see https://angular.dev/reference/migrations/self-closing-tags.', - 'To run the inject migration, execute the following command: ' + - '`ng generate @angular/core:inject`.\nFor more information, ' + - 'see https://angular.dev/reference/migrations/inject-function.', - ]; - - expect(instructions?.sort()).toEqual(expectedInstructions.sort()); + it('can run multiple transformations', async () => { + const { structuredContent } = (await runModernization( + { + directories: [projectDir], + transformations: ['control-flow', 'self-closing-tag'], + }, + mockHost, + )) as { structuredContent: ModernizeOutput }; + + expect(mockHost.runCommand).toHaveBeenCalledTimes(2); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:control-flow', '--path', '.'], + { + cwd: projectDir, + }, + ); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:self-closing-tag', '--path', '.'], + { cwd: projectDir }, + ); + expect(structuredContent?.stderr).toBeUndefined(); + expect(structuredContent?.instructions).toEqual( + jasmine.arrayWithExactContents([ + 'Migration control-flow on directory . completed successfully.', + 'Migration self-closing-tag on directory . completed successfully.', + ]), + ); + }); + + it('can run multiple transformations across multiple directories', async () => { + const subfolder1 = join(projectDir, 'subfolder1'); + const subfolder2 = join(projectDir, 'subfolder2'); + await mkdir(subfolder1); + await mkdir(subfolder2); + + const { structuredContent } = (await runModernization( + { + directories: [subfolder1, subfolder2], + transformations: ['control-flow', 'self-closing-tag'], + }, + mockHost, + )) as { structuredContent: ModernizeOutput }; + + expect(mockHost.runCommand).toHaveBeenCalledTimes(4); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:control-flow', '--path', 'subfolder1'], + { cwd: projectDir }, + ); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:self-closing-tag', '--path', 'subfolder1'], + { cwd: projectDir }, + ); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:control-flow', '--path', 'subfolder2'], + { cwd: projectDir }, + ); + expect(mockHost.runCommand).toHaveBeenCalledWith( + 'ng', + ['generate', '@angular/core:self-closing-tag', '--path', 'subfolder2'], + { cwd: projectDir }, + ); + expect(structuredContent?.stderr).toBeUndefined(); + expect(structuredContent?.instructions).toEqual( + jasmine.arrayWithExactContents([ + 'Migration control-flow on directory subfolder1 completed successfully.', + 'Migration self-closing-tag on directory subfolder1 completed successfully.', + 'Migration control-flow on directory subfolder2 completed successfully.', + 'Migration self-closing-tag on directory subfolder2 completed successfully.', + ]), + ); }); - it('should return a link to the best practices page when no transformations are requested', async () => { - const instructions = await getInstructions({ - transformations: [], - }); + it('should return an error if angular.json is not found', async () => { + (mockHost.existsSync as jasmine.Spy).and.returnValue(false); + + const { structuredContent } = (await runModernization( + { + directories: [projectDir], + transformations: ['self-closing-tag'], + }, + mockHost, + )) as { structuredContent: ModernizeOutput }; - expect(instructions).toEqual([ - 'See https://angular.dev/best-practices for Angular best practices. You can call this ' + - 'tool if you have specific transformation you want to run.', + expect(mockHost.runCommand).not.toHaveBeenCalled(); + expect(structuredContent?.instructions).toEqual([ + 'Could not find an angular.json file in the current or parent directories.', ]); }); - it('should return special instructions for standalone migration', async () => { - const instructions = await getInstructions({ - transformations: ['standalone'], - }); + it('should report errors from transformations', async () => { + // Simulate a failed execution + (mockHost.runCommand as jasmine.Spy).and.rejectWith( + new host.CommandError('Command failed with error', 'stdout', 'stderr', 1), + ); + + const { structuredContent } = (await runModernization( + { + directories: [projectDir], + transformations: ['self-closing-tag'], + }, + mockHost, + )) as { structuredContent: ModernizeOutput }; - expect(instructions?.[0]).toContain( - 'Run the commands in the order listed below, verifying that your code builds and runs between each step:', + expect(mockHost.runCommand).toHaveBeenCalledOnceWith( + 'ng', + ['generate', '@angular/core:self-closing-tag', '--path', '.'], + { cwd: projectDir }, ); + expect(structuredContent?.stdout).toContain('stdout'); + expect(structuredContent?.stderr).toContain('stderr'); + expect(structuredContent?.stderr).toContain('Command failed with error'); + expect(structuredContent?.instructions).toEqual([ + 'Migration self-closing-tag on directory . failed.', + ]); }); }); diff --git a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts index eaca30e274d9..3e6fedd5abd7 100644 --- a/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts +++ b/packages/angular/cli/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.ts @@ -21,7 +21,7 @@ import { sendDebugMessage } from './send_debug_message'; import { createSourceFile, getImportSpecifier } from './ts_utils'; export const ZONELESS_MIGRATION_TOOL = declareTool({ - name: 'onpush-zoneless-migration', + name: 'onpush_zoneless_migration', title: 'Plan migration to OnPush and/or zoneless', description: ` diff --git a/packages/angular/cli/src/commands/version/version-info.ts b/packages/angular/cli/src/commands/version/version-info.ts index ff3186c7b7ed..850cc18d0947 100644 --- a/packages/angular/cli/src/commands/version/version-info.ts +++ b/packages/angular/cli/src/commands/version/version-info.ts @@ -72,6 +72,7 @@ const PACKAGE_PATTERNS = [ /^rxjs$/, /^typescript$/, /^ng-packagr$/, + /^vitest$/, /^webpack$/, /^zone\.js$/, ]; diff --git a/packages/angular/cli/src/utilities/package-manager.ts b/packages/angular/cli/src/utilities/package-manager.ts index 54b5a21df4de..b913a3bfd72d 100644 --- a/packages/angular/cli/src/utilities/package-manager.ts +++ b/packages/angular/cli/src/utilities/package-manager.ts @@ -61,7 +61,7 @@ export class PackageManagerUtils { /** Install a single package. */ async install( packageName: string, - save: 'dependencies' | 'devDependencies' | true = true, + save: 'dependencies' | 'devDependencies' | boolean = true, extraArgs: string[] = [], cwd?: string, ): Promise { @@ -70,6 +70,8 @@ export class PackageManagerUtils { if (save === 'devDependencies') { installArgs.push(packageManagerArgs.saveDev); + } else if (save === false) { + installArgs.push(packageManagerArgs.noLockfile); } return this.run([...installArgs, ...extraArgs], { cwd, silent: true }); @@ -158,11 +160,11 @@ export class PackageManagerUtils { }; case PackageManager.Bun: return { - saveDev: '--development', + saveDev: '--dev', install: 'add', installAll: 'install', prefix: '--cwd', - noLockfile: '', + noLockfile: '--no-save', }; default: return { diff --git a/packages/angular/ssr/BUILD.bazel b/packages/angular/ssr/BUILD.bazel index 7475f48a4a6e..2fd35cf8adc3 100644 --- a/packages/angular/ssr/BUILD.bazel +++ b/packages/angular/ssr/BUILD.bazel @@ -20,7 +20,7 @@ ts_project( ), args = [ "--lib", - "dom,es2020", + "dom,es2022", ], data = [ "//packages/angular/ssr/third_party/beasties:beasties_bundled", @@ -61,7 +61,9 @@ ng_package( "//packages/angular/ssr/third_party/beasties:beasties_dts", ], package = "@angular/ssr", + readme_md = ":README.md", rollup_runtime_deps = [ + "//:node_modules/@babel/core", "//:node_modules/@rollup/plugin-commonjs", "//:node_modules/@rollup/plugin-node-resolve", "//:node_modules/magic-string", diff --git a/packages/angular/ssr/package.json b/packages/angular/ssr/package.json index 76b70f54ad5c..53303cf9d412 100644 --- a/packages/angular/ssr/package.json +++ b/packages/angular/ssr/package.json @@ -29,12 +29,12 @@ }, "devDependencies": { "@angular-devkit/schematics": "workspace:*", - "@angular/common": "21.0.0-next.9", - "@angular/compiler": "21.0.0-next.9", - "@angular/core": "21.0.0-next.9", - "@angular/platform-browser": "21.0.0-next.9", - "@angular/platform-server": "21.0.0-next.9", - "@angular/router": "21.0.0-next.9", + "@angular/common": "21.0.1", + "@angular/compiler": "21.0.1", + "@angular/core": "21.0.1", + "@angular/platform-browser": "21.0.1", + "@angular/platform-server": "21.0.1", + "@angular/router": "21.0.1", "@schematics/angular": "workspace:*", "beasties": "0.3.5" }, diff --git a/packages/angular/ssr/src/app-engine.ts b/packages/angular/ssr/src/app-engine.ts index 0ce5d23c30d1..dd204a4b595f 100644 --- a/packages/angular/ssr/src/app-engine.ts +++ b/packages/angular/ssr/src/app-engine.ts @@ -191,9 +191,10 @@ export class AngularAppEngine { * @returns A promise that resolves to the entry point exports or `undefined` if not found. */ private getEntryPointExportsForUrl(url: URL): Promise | undefined { - const { basePath } = this.manifest; + const { basePath, supportedLocales } = this.manifest; + if (this.supportedLocales.length === 1) { - return this.getEntryPointExports(''); + return this.getEntryPointExports(supportedLocales[this.supportedLocales[0]]); } const potentialLocale = getPotentialLocaleIdFromUrl(url, basePath); diff --git a/packages/angular/ssr/src/app.ts b/packages/angular/ssr/src/app.ts index 4895866d715b..ee14f8a26105 100644 --- a/packages/angular/ssr/src/app.ts +++ b/packages/angular/ssr/src/app.ts @@ -175,8 +175,15 @@ export class AngularServerApp { } const { redirectTo, status, renderMode } = matchedRoute; + if (redirectTo !== undefined) { - return createRedirectResponse(buildPathWithParams(redirectTo, url.pathname), status); + return createRedirectResponse( + joinUrlParts( + request.headers.get('X-Forwarded-Prefix') ?? '', + buildPathWithParams(redirectTo, url.pathname), + ), + status, + ); } if (renderMode === RenderMode.Prerender) { diff --git a/packages/angular/ssr/src/manifest.ts b/packages/angular/ssr/src/manifest.ts index 8fc415546033..0de603bba104 100644 --- a/packages/angular/ssr/src/manifest.ts +++ b/packages/angular/ssr/src/manifest.ts @@ -73,7 +73,7 @@ export interface AngularAppEngineManifest { * - `key`: The locale identifier (e.g., 'en', 'fr'). * - `value`: The url segment associated with that locale. */ - readonly supportedLocales: Readonly>; + readonly supportedLocales: Readonly>; } /** diff --git a/packages/angular/ssr/src/routes/ng-routes.ts b/packages/angular/ssr/src/routes/ng-routes.ts index 2a9281f40e9b..e46dd685511a 100644 --- a/packages/angular/ssr/src/routes/ng-routes.ts +++ b/packages/angular/ssr/src/routes/ng-routes.ts @@ -251,15 +251,22 @@ async function* traverseRoutesConfig(options: { const currentRoutePath = joinUrlParts(parentRoute, path); if (matcher && serverConfigRouteTree) { - let foundMatch = false; + const matches: (RouteTreeNodeMetadata & ServerConfigRouteTreeAdditionalMetadata)[] = []; for (const matchedMetaData of serverConfigRouteTree.traverse()) { - if (!matchedMetaData.route.startsWith(currentRoutePath)) { - continue; + if (matchedMetaData.route.startsWith(currentRoutePath)) { + matches.push(matchedMetaData); } + } - foundMatch = true; - matchedMetaData.presentInClientRouter = true; + if (!matches.length) { + const matchedMetaData = serverConfigRouteTree.match(currentRoutePath); + if (matchedMetaData) { + matches.push(matchedMetaData); + } + } + for (const matchedMetaData of matches) { + matchedMetaData.presentInClientRouter = true; if (matchedMetaData.renderMode === RenderMode.Prerender) { yield { error: @@ -282,7 +289,7 @@ async function* traverseRoutesConfig(options: { }); } - if (!foundMatch) { + if (!matches.length) { yield { error: `The route '${stripLeadingSlash(currentRoutePath)}' has a defined matcher but does not ` + diff --git a/packages/angular/ssr/src/utils/ng.ts b/packages/angular/ssr/src/utils/ng.ts index 120fdf940dd6..16e059e6aaf2 100644 --- a/packages/angular/ssr/src/utils/ng.ts +++ b/packages/angular/ssr/src/utils/ng.ts @@ -6,10 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import { PlatformLocation } from '@angular/common'; +import { APP_BASE_HREF, PlatformLocation } from '@angular/common'; import { ApplicationRef, type PlatformRef, + REQUEST, type StaticProvider, type Type, ɵConsole, @@ -23,7 +24,7 @@ import { } from '@angular/platform-server'; import { ActivatedRoute, Router } from '@angular/router'; import { Console } from '../console'; -import { stripIndexHtmlFromURL, stripTrailingSlash } from './url'; +import { addTrailingSlash, joinUrlParts, stripIndexHtmlFromURL, stripTrailingSlash } from './url'; /** * Represents the bootstrap mechanism for an Angular application. @@ -107,13 +108,19 @@ export async function renderAngular( if (!routerIsProvided) { hasNavigationError = false; - } else if (lastSuccessfulNavigation) { + } else if (lastSuccessfulNavigation?.finalUrl) { hasNavigationError = false; + + const requestPrefix = + envInjector.get(APP_BASE_HREF, null, { optional: true }) ?? + envInjector.get(REQUEST, null, { optional: true })?.headers.get('X-Forwarded-Prefix'); + const { pathname, search, hash } = envInjector.get(PlatformLocation); - const finalUrl = [stripTrailingSlash(pathname), search, hash].join(''); + const finalUrl = constructDecodedUrl({ pathname, search, hash }, requestPrefix); + const urlToRenderString = constructDecodedUrl(urlToRender, requestPrefix); - if (urlToRender.href !== new URL(finalUrl, urlToRender.origin).href) { - redirectTo = finalUrl; + if (urlToRenderString !== finalUrl) { + redirectTo = [pathname, search, hash].join(''); } } @@ -171,3 +178,36 @@ function asyncDestroyPlatform(platformRef: PlatformRef): Promise { }, 0); }); } + +/** + * Constructs a decoded URL string from its components, ensuring consistency for comparison. + * + * This function takes a URL-like object (containing `pathname`, `search`, and `hash`), + * strips the trailing slash from the pathname, joins the components, and then decodes + * the entire string. This normalization is crucial for accurately comparing URLs + * that might differ only in encoding or trailing slashes. + * + * @param url - An object containing the URL components: + * - `pathname`: The path of the URL. + * - `search`: The query string of the URL (including '?'). + * - `hash`: The hash fragment of the URL (including '#'). + * @param prefix - An optional prefix (e.g., `APP_BASE_HREF`) to prepend to the pathname + * if it is not already present. + * @returns The constructed and decoded URL string. + */ +function constructDecodedUrl( + url: { pathname: string; search: string; hash: string }, + prefix?: string | null, +): string { + const { pathname, hash, search } = url; + const urlParts: string[] = []; + if (prefix && !addTrailingSlash(pathname).startsWith(addTrailingSlash(prefix))) { + urlParts.push(joinUrlParts(prefix, pathname)); + } else { + urlParts.push(stripTrailingSlash(pathname)); + } + + urlParts.push(search, hash); + + return decodeURIComponent(urlParts.join('')); +} diff --git a/packages/angular/ssr/src/utils/url.ts b/packages/angular/ssr/src/utils/url.ts index 9b5edede7f8e..1fa756e19c19 100644 --- a/packages/angular/ssr/src/utils/url.ts +++ b/packages/angular/ssr/src/utils/url.ts @@ -22,7 +22,7 @@ */ export function stripTrailingSlash(url: string): string { // Check if the last character of the URL is a slash - return url.length > 1 && url[url.length - 1] === '/' ? url.slice(0, -1) : url; + return url.length > 1 && url.at(-1) === '/' ? url.slice(0, -1) : url; } /** @@ -75,7 +75,7 @@ export function addLeadingSlash(url: string): string { */ export function addTrailingSlash(url: string): string { // Check if the URL already end with a slash - return url[url.length - 1] === '/' ? url : `${url}/`; + return url.at(-1) === '/' ? url : `${url}/`; } /** @@ -106,7 +106,7 @@ export function joinUrlParts(...parts: string[]): string { if (part[0] === '/') { normalizedPart = normalizedPart.slice(1); } - if (part[part.length - 1] === '/') { + if (part.at(-1) === '/') { normalizedPart = normalizedPart.slice(0, -1); } if (normalizedPart !== '') { @@ -220,3 +220,18 @@ export function stripMatrixParams(pathname: string): string { // This regex finds all occurrences of a semicolon followed by any characters return pathname.includes(';') ? pathname.replace(MATRIX_PARAMS_REGEX, '') : pathname; } + +/** + * Constructs a decoded URL string from its components. + * + * This function joins the pathname (with trailing slash removed), search, and hash, + * and then decodes the result. + * + * @param pathname - The path of the URL. + * @param search - The query string of the URL (including '?'). + * @param hash - The hash fragment of the URL (including '#'). + * @returns The constructed and decoded URL string. + */ +export function constructUrl(pathname: string, search: string, hash: string): string { + return decodeURIComponent([stripTrailingSlash(pathname), search, hash].join('')); +} diff --git a/packages/angular/ssr/test/app-engine_spec.ts b/packages/angular/ssr/test/app-engine_spec.ts index 966edadce843..b08931b9400b 100644 --- a/packages/angular/ssr/test/app-engine_spec.ts +++ b/packages/angular/ssr/test/app-engine_spec.ts @@ -157,6 +157,66 @@ describe('AngularAppEngine', () => { }); }); + describe('Localized app with single locale', () => { + beforeAll(() => { + setAngularAppEngineManifest({ + entryPoints: { + it: createEntryPoint('it'), + }, + supportedLocales: { 'it': 'it' }, + basePath: '/', + }); + + appEngine = new AngularAppEngine(); + }); + + describe('handle', () => { + it('should return null for requests to unknown pages', async () => { + const request = new Request('https://example.com/unknown/page'); + const response = await appEngine.handle(request); + expect(response).toBeNull(); + }); + + it('should return a rendered page with correct locale', async () => { + const request = new Request('https://example.com/it/ssr'); + const response = await appEngine.handle(request); + expect(await response?.text()).toContain('SSR works IT'); + }); + + it('should correctly render the content when the URL ends with "index.html" with correct locale', async () => { + const request = new Request('https://example.com/it/ssr/index.html'); + const response = await appEngine.handle(request); + expect(await response?.text()).toContain('SSR works IT'); + expect(response?.headers?.get('Content-Language')).toBe('it'); + }); + + it('should return a serve prerendered page with correct locale', async () => { + const request = new Request('https://example.com/it/ssg'); + const response = await appEngine.handle(request); + expect(await response?.text()).toContain('SSG works IT'); + expect(response?.headers?.get('Content-Language')).toBe('it'); + }); + + it('should correctly serve the prerendered content when the URL ends with "index.html" with correct locale', async () => { + const request = new Request('https://example.com/it/ssg/index.html'); + const response = await appEngine.handle(request); + expect(await response?.text()).toContain('SSG works IT'); + }); + + it('should return null for requests to unknown pages in a locale', async () => { + const request = new Request('https://example.com/it/unknown/page'); + const response = await appEngine.handle(request); + expect(response).toBeNull(); + }); + + it('should return null for requests to file-like resources in a locale', async () => { + const request = new Request('https://example.com/it/logo.png'); + const response = await appEngine.handle(request); + expect(response).toBeNull(); + }); + }); + }); + describe('Non-localized app', () => { beforeAll(() => { @Component({ diff --git a/packages/angular/ssr/test/app_spec.ts b/packages/angular/ssr/test/app_spec.ts index f85d4700329f..46b7cebc4e8a 100644 --- a/packages/angular/ssr/test/app_spec.ts +++ b/packages/angular/ssr/test/app_spec.ts @@ -11,7 +11,8 @@ import '@angular/compiler'; /* eslint-enable import/no-unassigned-import */ -import { Component, inject } from '@angular/core'; +import { APP_BASE_HREF } from '@angular/common'; +import { Component, REQUEST, inject } from '@angular/core'; import { CanActivateFn, Router } from '@angular/router'; import { AngularServerApp } from '../src/app'; import { RenderMode } from '../src/routes/route-config'; @@ -124,6 +125,14 @@ describe('AngularServerApp', () => { hash: 'f799132d0a09e0fef93c68a12e443527700eb59e6f67fcb7854c3a60ff082fde', }, }, + undefined, + undefined, + [ + { + provide: APP_BASE_HREF, + useFactory: () => inject(REQUEST)?.headers.get('X-Forwarded-Prefix'), + }, + ], ); app = new AngularServerApp(); @@ -309,6 +318,50 @@ describe('AngularServerApp', () => { expect(response?.headers.get('location')).toBe('/redirect-via-guard?filter=test'); expect(response?.status).toBe(302); }); + + it('should work with encoded characters', async () => { + const request = new Request('http://localhost/home?email=xyz%40xyz.com'); + const response = await app.handle(request); + expect(response?.status).toBe(200); + expect(await response?.text()).toContain('Home works'); + }); + + it('should work with decoded characters', async () => { + const request = new Request('http://localhost/home?email=xyz@xyz.com'); + const response = await app.handle(request); + expect(response?.status).toBe(200); + expect(await response?.text()).toContain('Home works'); + }); + + describe('APP_BASE_HREF / X-Forwarded-Prefix', () => { + const headers = new Headers({ 'X-Forwarded-Prefix': '/base/' }); + + it('should return a rendered page for known paths', async () => { + const request = new Request('https://example.com/home', { headers }); + const response = await app.handle(request); + expect(await response?.text()).toContain('Home works'); + }); + + it('returns a 302 status and redirects to the correct location when `redirectTo` is a function', async () => { + const response = await app.handle( + new Request('http://localhost/redirect-to-function', { + headers, + }), + ); + expect(response?.headers.get('location')).toBe('/base/home'); + expect(response?.status).toBe(302); + }); + + it('returns a 302 status and redirects to the correct location when `redirectTo` is a string', async () => { + const response = await app.handle( + new Request('http://localhost/redirect', { + headers, + }), + ); + expect(response?.headers.get('location')).toBe('/base/home'); + expect(response?.status).toBe(302); + }); + }); }); }); }); diff --git a/packages/angular/ssr/test/npm_package/BUILD.bazel b/packages/angular/ssr/test/npm_package/BUILD.bazel index d79cbf7d0105..ae1694d8caac 100644 --- a/packages/angular/ssr/test/npm_package/BUILD.bazel +++ b/packages/angular/ssr/test/npm_package/BUILD.bazel @@ -1,5 +1,4 @@ -load("@bazel_skylib//rules:diff_test.bzl", "diff_test") -load("@bazel_skylib//rules:write_file.bzl", "write_file") +load("@aspect_bazel_lib//lib:write_source_files.bzl", "write_source_file") load("//tools:defaults.bzl", "jasmine_test", "ts_project") ts_project( @@ -32,26 +31,8 @@ genrule( """, ) -diff_test( - name = "beasties_license_test", - failure_message = """ - - To accept the new golden file, execute: - pnpm bazel run //packages/angular/ssr/test/npm_package:beasties_license_test.accept - """, - file1 = ":THIRD_PARTY_LICENSES.txt.golden", - file2 = ":beasties_license_file", -) - -write_file( - name = "beasties_license_test.accept", - out = "beasties_license_file_accept.sh", - content = - [ - "#!/usr/bin/env bash", - "cd ${BUILD_WORKSPACE_DIRECTORY}", - "pnpm bazel build //packages/angular/ssr:npm_package", - "cp -fv dist/bin/packages/angular/ssr/npm_package/third_party/beasties/THIRD_PARTY_LICENSES.txt packages/angular/ssr/test/npm_package/THIRD_PARTY_LICENSES.txt.golden", - ], - is_executable = True, +write_source_file( + name = "beasties_license", + in_file = ":beasties_license_file", + out_file = ":THIRD_PARTY_LICENSES.txt.golden", ) diff --git a/packages/angular/ssr/test/routes/ng-routes_spec.ts b/packages/angular/ssr/test/routes/ng-routes_spec.ts index 628546432d0c..324abe8c4d29 100644 --- a/packages/angular/ssr/test/routes/ng-routes_spec.ts +++ b/packages/angular/ssr/test/routes/ng-routes_spec.ts @@ -428,6 +428,45 @@ describe('extractRoutesAndCreateRouteTree', () => { ]); }); + it('should extract routes with a route level matcher captured by "**"', async () => { + setAngularAppTestingManifest( + [ + { + path: '', + component: DummyComponent, + }, + { + path: 'list', + component: DummyComponent, + }, + { + path: 'product', + component: DummyComponent, + children: [ + { + matcher: () => null, + component: DummyComponent, + }, + ], + }, + ], + [ + { path: 'list', renderMode: RenderMode.Client }, + { path: '', renderMode: RenderMode.Client }, + { path: '**', renderMode: RenderMode.Server }, + ], + ); + + const { routeTree, errors } = await extractRoutesAndCreateRouteTree({ url }); + expect(errors).toHaveSize(0); + expect(routeTree.toObject()).toEqual([ + { route: '/', renderMode: RenderMode.Client }, + { route: '/list', renderMode: RenderMode.Client }, + { route: '/product', renderMode: RenderMode.Server }, + { route: '/**', renderMode: RenderMode.Server }, + ]); + }); + it('should extract nested redirects that are not explicitly defined.', async () => { setAngularAppTestingManifest( [ diff --git a/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts b/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts index a4b1ee75a922..560927179294 100644 --- a/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts +++ b/packages/angular_devkit/architect/src/jobs/simple-scheduler_spec.ts @@ -168,7 +168,7 @@ describe('SimpleScheduler', () => { // Might be out of order. expect(done).toEqual(jasmine.arrayContaining([1, 2, 3, 4, 5, 6, 7])); // Verify at least partial order. - expect(done[done.length - 1]).toBe(7); + expect(done.at(-1)).toBe(7); expect(done.indexOf(4)).toBeGreaterThan(done.indexOf(1)); expect(done.indexOf(5)).toBeGreaterThan(done.indexOf(2)); expect(done.indexOf(6)).toBeGreaterThan(done.indexOf(3)); diff --git a/packages/angular_devkit/architect/testing/testing-architect-host.ts b/packages/angular_devkit/architect/testing/testing-architect-host.ts index 2f61d8f69d9d..521c854921c0 100644 --- a/packages/angular_devkit/architect/testing/testing-architect-host.ts +++ b/packages/angular_devkit/architect/testing/testing-architect-host.ts @@ -72,7 +72,7 @@ export class TestingArchitectHost implements ArchitectHost { const name = targetStringFromTarget(target); const maybeTarget = this._targetMap.get(name); if (!maybeTarget) { - return this._backendHost && this._backendHost.getBuilderNameForTarget(target); + return this._backendHost?.getBuilderNameForTarget(target) ?? null; } return maybeTarget.builderName; @@ -87,8 +87,7 @@ export class TestingArchitectHost implements ArchitectHost { */ async resolveBuilder(builderName: string): Promise { return ( - this._builderMap.get(builderName) || - (this._backendHost && this._backendHost.resolveBuilder(builderName)) + this._builderMap.get(builderName) || (this._backendHost?.resolveBuilder(builderName) ?? null) ); } @@ -103,20 +102,19 @@ export class TestingArchitectHost implements ArchitectHost { const name = targetStringFromTarget(target); const maybeTarget = this._targetMap.get(name); if (!maybeTarget) { - return this._backendHost && this._backendHost.getOptionsForTarget(target); + return this._backendHost?.getOptionsForTarget(target) ?? null; } return maybeTarget.options; } async getProjectMetadata(target: Target | string): Promise { - return this._backendHost && this._backendHost.getProjectMetadata(target as string); + return this._backendHost?.getProjectMetadata(target as string) ?? null; } async loadBuilder(info: BuilderInfo): Promise { return ( - this._builderImportMap.get(info.builderName) || - (this._backendHost && this._backendHost.loadBuilder(info)) + this._builderImportMap.get(info.builderName) || (this._backendHost?.loadBuilder(info) ?? null) ); } } diff --git a/packages/angular_devkit/build_angular/BUILD.bazel b/packages/angular_devkit/build_angular/BUILD.bazel index 7654aeadf12a..2a505e1fd8ca 100644 --- a/packages/angular_devkit/build_angular/BUILD.bazel +++ b/packages/angular_devkit/build_angular/BUILD.bazel @@ -226,7 +226,6 @@ ts_project( ":build_angular", ":build_angular_test_utils", ":node_modules/tinyglobby", - ":node_modules/webpack", "//:node_modules/@types/node", "//:node_modules/prettier", "//:node_modules/typescript", @@ -392,8 +391,6 @@ LARGE_SPECS = { ":node_modules/@angular/build", ":node_modules/@angular-devkit/architect", ":node_modules/@angular-devkit/core", - ":node_modules/@angular-devkit/build-webpack", - "//modules/testing/builder", # Base dependencies for the application in hello-world-app. # Some tests also require extra dependencies. diff --git a/packages/angular_devkit/build_angular/package.json b/packages/angular_devkit/build_angular/package.json index 766a82b791fd..b64441ef8ae8 100644 --- a/packages/angular_devkit/build_angular/package.json +++ b/packages/angular_devkit/build_angular/package.json @@ -28,7 +28,7 @@ "browserslist": "^4.26.0", "copy-webpack-plugin": "13.0.1", "css-loader": "7.1.2", - "esbuild-wasm": "0.25.11", + "esbuild-wasm": "0.26.0", "http-proxy-middleware": "3.0.5", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", @@ -62,13 +62,13 @@ "webpack-subresource-integrity": "5.1.0" }, "optionalDependencies": { - "esbuild": "0.25.11" + "esbuild": "0.26.0" }, "devDependencies": { "@angular/ssr": "workspace:*", "@web/test-runner": "0.20.2", "browser-sync": "3.0.4", - "ng-packagr": "21.0.0-next.4", + "ng-packagr": "21.0.0", "undici": "7.16.0" }, "peerDependencies": { diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts index aa5b84f324aa..d1dfc6c90e00 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/builder.ts @@ -97,6 +97,7 @@ export function execute( normalizedOptions as typeof normalizedOptions & { hmr: boolean; allowedHosts: true | string[]; + define: { [key: string]: string } | undefined; }, builderName, (options, context, codePlugins) => { diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/specs/ssl_spec.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/specs/ssl_spec.ts index 1f41eba74279..60ed65793c7f 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/specs/ssl_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/specs/ssl_spec.ts @@ -8,7 +8,7 @@ import { Architect, BuilderRun } from '@angular-devkit/architect'; import { tags } from '@angular-devkit/core'; -import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici'; +import { Agent } from 'undici'; import { createArchitect, host } from '../../../testing/test-utils'; import { DevServerBuilderOutput } from '../index'; @@ -35,20 +35,12 @@ describe('Dev Server Builder ssl', () => { expect(output.success).toBe(true); expect(output.baseUrl).toMatch(/^https:\/\/localhost:\d+\//); - // The self-signed certificate used by the dev server will cause fetch to fail - // unless reject unauthorized is disabled. - const originalDispatcher = getGlobalDispatcher(); - setGlobalDispatcher( - new Agent({ + const response = await fetch(output.baseUrl, { + dispatcher: new Agent({ connect: { rejectUnauthorized: false }, }), - ); - try { - const response = await fetch(output.baseUrl); - expect(await response.text()).toContain('HelloWorldApp'); - } finally { - setGlobalDispatcher(originalDispatcher); - } + }); + expect(await response.text()).toContain('HelloWorldApp'); }); it('supports key and cert', async () => { @@ -122,19 +114,11 @@ describe('Dev Server Builder ssl', () => { expect(output.success).toBe(true); expect(output.baseUrl).toMatch(/^https:\/\/localhost:\d+\//); - // The self-signed certificate used by the dev server will cause fetch to fail - // unless reject unauthorized is disabled. - const originalDispatcher = getGlobalDispatcher(); - setGlobalDispatcher( - new Agent({ + const response = await fetch(output.baseUrl, { + dispatcher: new Agent({ connect: { rejectUnauthorized: false }, }), - ); - try { - const response = await fetch(output.baseUrl); - expect(await response.text()).toContain('HelloWorldApp'); - } finally { - setGlobalDispatcher(originalDispatcher); - } + }); + expect(await response.text()).toContain('HelloWorldApp'); }); }); diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts index c2a1758f2b5e..09d50dbb4528 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/behavior/serve-live-reload-proxies_spec.ts @@ -108,8 +108,7 @@ async function createProxy(target: string, secure: boolean, ws = true): Promise< async function goToPageAndWaitForWS(page: Page, url: string): Promise { const baseUrl = url.replace(/^http/, 'ws'); - const socksRequest = - baseUrl[baseUrl.length - 1] === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; + const socksRequest = baseUrl.at(-1) === '/' ? `${baseUrl}ng-cli-ws` : `${baseUrl}/ng-cli-ws`; // Create a Chrome dev tools session so that we can capturing websocket request. // https://github.com/puppeteer/puppeteer/issues/2974 diff --git a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts index eada7975ba88..489ae5501cda 100644 --- a/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts +++ b/packages/angular_devkit/build_angular/src/builders/dev-server/tests/execute-fetch.ts @@ -27,7 +27,7 @@ export async function executeOnceAndFetch( let content = undefined; if (executionResult.result?.success) { let baseUrl = `${executionResult.result.baseUrl}`; - baseUrl = baseUrl[baseUrl.length - 1] === '/' ? baseUrl : `${baseUrl}/`; + baseUrl = baseUrl.at(-1) === '/' ? baseUrl : `${baseUrl}/`; const resolvedUrl = new URL(url, baseUrl); const originalResponse = await fetch(resolvedUrl, options?.request); response = originalResponse.clone(); diff --git a/packages/angular_devkit/build_angular/src/builders/jest/index.ts b/packages/angular_devkit/build_angular/src/builders/jest/index.ts index 5f91f70e589e..5cd8d6ebdad9 100644 --- a/packages/angular_devkit/build_angular/src/builders/jest/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/jest/index.ts @@ -26,7 +26,7 @@ const execFile = promisify(execFileCb); export default createBuilder( async (schema: JestBuilderSchema, context: BuilderContext): Promise => { context.logger.warn( - 'NOTE: The Jest builder is currently EXPERIMENTAL and not ready for production use.', + 'NOTE: The Jest builder is currently EXPERIMENTAL and will be removed in version 22.', ); const options = normalizeOptions(schema); diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts index 6219ea6a46a2..8ec879993e4f 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/index.ts @@ -21,6 +21,7 @@ import type { MiddlewareHandler, ProxyOptions, } from 'browser-sync'; +import { createRequire } from 'node:module'; import { join, resolve as pathResolve } from 'node:path'; import * as url from 'node:url'; import { @@ -64,7 +65,7 @@ export function execute( ): Observable { let browserSync: typeof import('browser-sync'); try { - browserSync = require('browser-sync'); + browserSync = createRequire(context.workspaceRoot + '/')('browser-sync'); } catch { return of({ success: false, diff --git a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts index a67cf0b3c5b5..7651b2387c16 100644 --- a/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts +++ b/packages/angular_devkit/build_angular/src/builders/ssr-dev-server/specs/ssl_spec.ts @@ -9,7 +9,7 @@ import { Architect } from '@angular-devkit/architect'; // eslint-disable-next-line import/no-extraneous-dependencies import * as browserSync from 'browser-sync'; -import { Agent, getGlobalDispatcher, setGlobalDispatcher } from 'undici'; +import { Agent } from 'undici'; import { createArchitect, host } from '../../../testing/test-utils'; import { SSRDevServerBuilderOutput } from '../index'; @@ -85,20 +85,13 @@ describe('Serve SSR Builder', () => { expect(output.success).toBe(true); expect(output.baseUrl).toBe(`https://localhost:${output.port}`); - // The self-signed certificate used by the dev server will cause fetch to fail - // unless reject unauthorized is disabled. - const originalDispatcher = getGlobalDispatcher(); - setGlobalDispatcher( - new Agent({ + const response = await fetch(`https://localhost:${output.port}/index.html`, { + dispatcher: new Agent({ connect: { rejectUnauthorized: false }, }), - ); - try { - const response = await fetch(`https://localhost:${output.port}/index.html`); - expect(await response.text()).toContain('HelloWorldApp'); - } finally { - setGlobalDispatcher(originalDispatcher); - } + }); + + expect(await response.text()).toContain('HelloWorldApp'); await run.stop(); }); diff --git a/packages/angular_devkit/build_angular/src/builders/web-test-runner/index.ts b/packages/angular_devkit/build_angular/src/builders/web-test-runner/index.ts index 066ed905760e..d900375221ff 100644 --- a/packages/angular_devkit/build_angular/src/builders/web-test-runner/index.ts +++ b/packages/angular_devkit/build_angular/src/builders/web-test-runner/index.ts @@ -23,8 +23,9 @@ import { writeTestFiles } from './write-test-files'; export default createBuilder( async (schema: Schema, ctx: BuilderContext): Promise => { ctx.logger.warn( - 'NOTE: The Web Test Runner builder is currently EXPERIMENTAL and not ready for production use.', + 'NOTE: The Web Test Runner builder is currently EXPERIMENTAL and will be removed in version 22.', ); + logBuilderStatusWarnings(schema, ctx); // Dynamic import `@web/test-runner` from the user's workspace. As an optional peer dep, it may not be installed diff --git a/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts b/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts index f078b614796c..66a2f09a6422 100644 --- a/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts +++ b/packages/angular_devkit/build_webpack/src/builders/webpack-dev-server/index.ts @@ -10,8 +10,8 @@ import { Builder, BuilderContext, createBuilder } from '@angular-devkit/architec import assert from 'node:assert'; import { resolve as pathResolve } from 'node:path'; import { Observable, from, isObservable, of, switchMap } from 'rxjs'; -import webpack from 'webpack'; -import WebpackDevServer from 'webpack-dev-server'; +import type webpack from 'webpack'; +import type WebpackDevServer from 'webpack-dev-server'; import { getEmittedFiles, getWebpackConfig } from '../../utils'; import { BuildResult, WebpackFactory, WebpackLoggingCallback } from '../webpack'; import { Schema as WebpackDevServerBuilderSchema } from './schema'; @@ -44,7 +44,7 @@ export function runWebpackDevServer( return of(result); } } else { - return of(webpack(c)); + return from(import('webpack').then((mod) => mod.default(c))); } }; @@ -54,9 +54,9 @@ export function runWebpackDevServer( ) => { if (options.webpackDevServerFactory) { return new options.webpackDevServerFactory(config, webpack); + } else { + return from(import('webpack-dev-server').then((mod) => new mod.default(config, webpack))); } - - return new WebpackDevServer(config, webpack); }; const { @@ -70,8 +70,14 @@ export function runWebpackDevServer( } = options; return createWebpack({ ...config, watch: false }).pipe( + switchMap(async (webpackCompiler) => { + return [ + webpackCompiler, + options.webpackDevServerFactory ?? (await import('webpack-dev-server')).default, + ] as unknown as [webpack.Compiler | null, WebpackDevServerFactory]; + }), switchMap( - (webpackCompiler) => + ([webpackCompiler, webpackDevServerFactory]) => new Observable((obs) => { assert(webpackCompiler, 'Webpack compiler factory did not return a compiler instance.'); @@ -79,7 +85,6 @@ export function runWebpackDevServer( devServerConfig.host ??= 'localhost'; let result: Partial; - const statsOptions = typeof config.stats === 'boolean' ? undefined : config.stats; webpackCompiler.hooks.done.tap('build-webpack', (stats) => { @@ -94,7 +99,7 @@ export function runWebpackDevServer( } as unknown as DevServerBuildOutput); }); - const devServer = createWebpackDevServer(webpackCompiler, devServerConfig); + const devServer = new webpackDevServerFactory(devServerConfig, webpackCompiler); devServer.startCallback((err) => { if (err) { obs.error(err); diff --git a/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts b/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts index 166a6385a0e1..ce3f91fd69d4 100644 --- a/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts +++ b/packages/angular_devkit/build_webpack/src/builders/webpack/index.ts @@ -10,7 +10,7 @@ import { Builder, BuilderContext, BuilderOutput, createBuilder } from '@angular- import assert from 'node:assert'; import { resolve as pathResolve } from 'node:path'; import { Observable, from, isObservable, of, switchMap } from 'rxjs'; -import webpack from 'webpack'; +import type webpack from 'webpack'; import { EmittedFiles, getEmittedFiles, getWebpackConfig } from '../../utils'; import { Schema as RealWebpackBuilderSchema } from './schema'; @@ -57,7 +57,7 @@ export function runWebpack( return of(result); } } else { - return of(webpack(c)); + return from(import('webpack').then((mod) => mod.default(c))); } }; diff --git a/packages/angular_devkit/core/src/utils/template.ts b/packages/angular_devkit/core/src/utils/template.ts index ebd3778fd4e9..f28bbf7ad417 100644 --- a/packages/angular_devkit/core/src/utils/template.ts +++ b/packages/angular_devkit/core/src/utils/template.ts @@ -253,9 +253,7 @@ function templateWithSourceMap(ast: TemplateAst, options?: TemplateOptions): str ]), ); - const end = ast.children.length - ? ast.children[ast.children.length - 1].end - : { line: 0, column: 0 }; + const end = ast.children.at(-1)?.end ?? { line: 0, column: 0 }; const nodes = ast.children .reduce((chunk, node) => { let code: string | SourceNode | (SourceNode | string)[] = ''; diff --git a/packages/angular_devkit/core/src/virtual-fs/host/buffer.ts b/packages/angular_devkit/core/src/virtual-fs/host/buffer.ts index 576d7a07a57f..3cb848f6b641 100644 --- a/packages/angular_devkit/core/src/virtual-fs/host/buffer.ts +++ b/packages/angular_devkit/core/src/virtual-fs/host/buffer.ts @@ -10,7 +10,7 @@ import { TextDecoder, TextEncoder } from 'node:util'; import { FileBuffer } from './interface'; export function stringToFileBuffer(str: string): FileBuffer { - return new TextEncoder().encode(str).buffer as FileBuffer; + return new TextEncoder().encode(str).buffer; } export function fileBufferToString(fileBuffer: FileBuffer): string { diff --git a/packages/angular_devkit/core/src/virtual-fs/path.ts b/packages/angular_devkit/core/src/virtual-fs/path.ts index 198b07aa7975..783bfe9d4402 100644 --- a/packages/angular_devkit/core/src/virtual-fs/path.ts +++ b/packages/angular_devkit/core/src/virtual-fs/path.ts @@ -57,7 +57,7 @@ export const NormalizedRoot: Path = NormalizedSep; */ export function split(path: Path): PathFragment[] { const fragments = path.split(NormalizedSep).map((x) => fragment(x)); - if (fragments[fragments.length - 1].length === 0) { + if (fragments.at(-1)?.length === 0) { fragments.pop(); } diff --git a/packages/angular_devkit/schematics/src/tree/action.ts b/packages/angular_devkit/schematics/src/tree/action.ts index 2f5f5e38e900..2d76bcc3662e 100644 --- a/packages/angular_devkit/schematics/src/tree/action.ts +++ b/packages/angular_devkit/schematics/src/tree/action.ts @@ -31,7 +31,7 @@ export class ActionList implements Iterable { this._actions.push({ ...(action as Action), id: _id++, - parent: this._actions[this._actions.length - 1]?.id ?? 0, + parent: this._actions.at(-1)?.id ?? 0, }); } diff --git a/packages/angular_devkit/schematics/src/workflow/base.ts b/packages/angular_devkit/schematics/src/workflow/base.ts index 66f1f20ec379..dcf23ff3f755 100644 --- a/packages/angular_devkit/schematics/src/workflow/base.ts +++ b/packages/angular_devkit/schematics/src/workflow/base.ts @@ -91,7 +91,7 @@ export abstract class BaseWorkflow implements Workflow { } get context(): Readonly { - const maybeContext = this._context[this._context.length - 1]; + const maybeContext = this._context.at(-1); if (!maybeContext) { throw new Error('Cannot get context when workflow is not executing...'); } @@ -146,7 +146,7 @@ export abstract class BaseWorkflow implements Workflow { execute( options: Partial & RequiredWorkflowExecutionContext, ): Observable { - const parentContext = this._context[this._context.length - 1]; + const parentContext = this._context.at(-1); if (!parentContext) { this._lifeCycle.next({ kind: 'start' }); diff --git a/packages/ngtools/webpack/package.json b/packages/ngtools/webpack/package.json index 438231fed1a6..7febec4638b5 100644 --- a/packages/ngtools/webpack/package.json +++ b/packages/ngtools/webpack/package.json @@ -27,8 +27,8 @@ }, "devDependencies": { "@angular-devkit/core": "workspace:0.0.0-PLACEHOLDER", - "@angular/compiler": "21.0.0-next.9", - "@angular/compiler-cli": "21.0.0-next.9", + "@angular/compiler": "21.0.1", + "@angular/compiler-cli": "21.0.1", "typescript": "5.9.3", "webpack": "5.102.1" } diff --git a/packages/schematics/angular/BUILD.bazel b/packages/schematics/angular/BUILD.bazel index b3fa1e537720..27e1179fa107 100644 --- a/packages/schematics/angular/BUILD.bazel +++ b/packages/schematics/angular/BUILD.bazel @@ -45,11 +45,24 @@ copy_to_bin( srcs = glob(["**/schema.json"]), ) +genrule( + name = "angular_best_practices", + srcs = [ + "//:node_modules/@angular/core/dir", + ], + outs = ["ai-config/files/__rulesName__.template"], + cmd = """ + echo -e "<% if (frontmatter) { %><%= frontmatter %>\\n<% } %>" > $@ + cat "$(location //:node_modules/@angular/core/dir)/resources/best-practices.md" >> $@ + """, +) + RUNTIME_ASSETS = [ "collection.json", "migrations/migration-collection.json", "package.json", "utility/latest-versions/package.json", + ":angular_best_practices", ] + glob( include = [ "*/schema.json", @@ -115,13 +128,12 @@ ts_project( include = [ "**/*_spec.ts", "utility/test/**/*.ts", - "refactor/jasmine-vitest/test-helpers.ts", ], exclude = [ # NB: we need to exclude the nested node_modules that is laid out by yarn workspaces "node_modules/**", ], - ), + ) + ["refactor/jasmine-vitest/test-helpers.ts"], deps = [ ":angular", ":node_modules/@angular-devkit/core", diff --git a/packages/schematics/angular/ai-config/files/__rulesName__.template b/packages/schematics/angular/ai-config/files/__rulesName__.template deleted file mode 100644 index 0d4f1bbf8d41..000000000000 --- a/packages/schematics/angular/ai-config/files/__rulesName__.template +++ /dev/null @@ -1,49 +0,0 @@ -<% if (frontmatter) { %><%= frontmatter %> - -<% } %>You are an expert in TypeScript, Angular, and scalable web application development. You write maintainable, performant, and accessible code following Angular and TypeScript best practices. - -## TypeScript Best Practices - -- Use strict type checking -- Prefer type inference when the type is obvious -- Avoid the `any` type; use `unknown` when type is uncertain - -## Angular Best Practices - -- Always use standalone components over NgModules -- Must NOT set `standalone: true` inside Angular decorators. It's the default. -- Use signals for state management -- Implement lazy loading for feature routes -- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead -- Use `NgOptimizedImage` for all static images. - - `NgOptimizedImage` does not work for inline base64 images. - -## Components - -- Keep components small and focused on a single responsibility -- Use `input()` and `output()` functions instead of decorators -- Use `computed()` for derived state -- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator -- Prefer inline templates for small components -- Prefer Reactive forms instead of Template-driven ones -- Do NOT use `ngClass`, use `class` bindings instead -- Do NOT use `ngStyle`, use `style` bindings instead - -## State Management - -- Use signals for local component state -- Use `computed()` for derived state -- Keep state transformations pure and predictable -- Do NOT use `mutate` on signals, use `update` or `set` instead - -## Templates - -- Keep templates simple and avoid complex logic -- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch` -- Use the async pipe to handle observables - -## Services - -- Design services around a single responsibility -- Use the `providedIn: 'root'` option for singleton services -- Use the `inject()` function instead of constructor injection diff --git a/packages/schematics/angular/application/files/common-files/src/app/app__suffix__.html.template b/packages/schematics/angular/application/files/common-files/src/app/app__suffix__.html.template index b706f5bff17e..7d9cf8841c9d 100644 --- a/packages/schematics/angular/application/files/common-files/src/app/app__suffix__.html.template +++ b/packages/schematics/angular/application/files/common-files/src/app/app__suffix__.html.template @@ -286,8 +286,8 @@ @@ -297,7 +297,7 @@ viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" - alt="Twitter" + alt="X" > /tsconfig.json", "compilerOptions": { "outDir": "<%= relativePathToWorkspaceRoot %>/out-tsc/spec", - "types": [ - "<%= testRunner === 'vitest' ? 'vitest/globals' : 'jasmine' %>" - ] }, + "types": [ + "<%= testRunner === 'vitest' ? 'vitest/globals' : 'jasmine' %>" + ] + }, "include": [ - "src/**/*.ts" + "src/**/*.d.ts", + "src/**/*<% if (standalone) { %>.spec<% } %>.ts" ] } diff --git a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template index 4d3e1965efea..dfe31b1010c6 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.spec.ts.template @@ -1,6 +1,6 @@ import { TestBed } from '@angular/core/testing';<% if (routing) { %> import { RouterModule } from '@angular/router';<% } %> -import { App } from './app'; +import { App } from './app<%= suffix %>'; describe('App', () => { beforeEach(async () => { diff --git a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template index 2bb65ba29ba7..cbc072878c68 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app__suffix__.ts.template @@ -9,10 +9,10 @@ import { Component, signal } from '@angular/core'; %><% } %> `,<% } else { %> - templateUrl: './app.html',<% } %> + templateUrl: './app<%= suffix %>.html',<% } %> standalone: false,<% if(inlineStyle) { %> styles: []<% } else { %> - styleUrl: './app.<%= style %>'<% } %> + styleUrl: './app<%= suffix %>.<%= style %>'<% } %> }) export class App { protected readonly title = signal('<%= name %>'); diff --git a/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template b/packages/schematics/angular/application/files/module-files/src/app/app__typeSeparator__module.ts.template similarity index 76% rename from packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template rename to packages/schematics/angular/application/files/module-files/src/app/app__typeSeparator__module.ts.template index 8b91651c49d5..06ba3eb2a079 100644 --- a/packages/schematics/angular/application/files/module-files/src/app/app-module.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/app/app__typeSeparator__module.ts.template @@ -1,8 +1,8 @@ import { NgModule, provideBrowserGlobalErrorListeners } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; <% if (routing) { %> -import { AppRoutingModule } from './app-routing-module';<% } %> -import { App } from './app'; +import { AppRoutingModule } from './app-routing<%= typeSeparator %>module';<% } %> +import { App } from './app<%= suffix %>'; @NgModule({ declarations: [ diff --git a/packages/schematics/angular/application/files/module-files/src/main.ts.template b/packages/schematics/angular/application/files/module-files/src/main.ts.template index 407a18d4a65d..629780834c82 100644 --- a/packages/schematics/angular/application/files/module-files/src/main.ts.template +++ b/packages/schematics/angular/application/files/module-files/src/main.ts.template @@ -1,6 +1,6 @@ <% if(!!viewEncapsulation) { %>import { ViewEncapsulation } from '@angular/core'; <% }%>import { platformBrowser } from '@angular/platform-browser'; -import { AppModule } from './app/app-module'; +import { AppModule } from './app/app<%= typeSeparator %>module'; platformBrowser().bootstrapModule(AppModule, { <% if(!zoneless) { %>ngZoneEventCoalescing: true,<% } %><% if(!!viewEncapsulation) { %> diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template index af22780db300..e6944dc73ccd 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.spec.ts.template @@ -1,5 +1,5 @@ import { TestBed } from '@angular/core/testing'; -import { App } from './app'; +import { App } from './app<%= suffix %>'; describe('App', () => { beforeEach(async () => { diff --git a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template index 71e7e0bffc24..0e6ebd5930a7 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/app/app__suffix__.ts.template @@ -11,9 +11,9 @@ import { RouterOutlet } from '@angular/router';<% } %> %><% } %> `,<% } else { %> - templateUrl: './app.html',<% } if(inlineStyle) { %> + templateUrl: './app<%= suffix %>.html',<% } if(inlineStyle) { %> styles: [],<% } else { %> - styleUrl: './app.<%= style %>'<% } %> + styleUrl: './app<%= suffix %>.<%= style %>'<% } %> }) export class App { protected readonly title = signal('<%= name %>'); diff --git a/packages/schematics/angular/application/files/standalone-files/src/main.ts.template b/packages/schematics/angular/application/files/standalone-files/src/main.ts.template index 5df75f9c838e..104fe6b29ae7 100644 --- a/packages/schematics/angular/application/files/standalone-files/src/main.ts.template +++ b/packages/schematics/angular/application/files/standalone-files/src/main.ts.template @@ -1,6 +1,6 @@ import { bootstrapApplication } from '@angular/platform-browser'; import { appConfig } from './app/app.config'; -import { App } from './app/app'; +import { App } from './app/app<%= suffix %>'; bootstrapApplication(App, appConfig) .catch((err) => console.error(err)); diff --git a/packages/schematics/angular/application/index.ts b/packages/schematics/angular/application/index.ts index 295c65ba93ca..e84a40530032 100644 --- a/packages/schematics/angular/application/index.ts +++ b/packages/schematics/angular/application/index.ts @@ -23,6 +23,7 @@ import { url, } from '@angular-devkit/schematics'; import { Schema as ComponentOptions, Style as ComponentStyle } from '../component/schema'; +import { addTestRunnerDependencies } from '../utility/dependencies'; import { DependencyType, ExistingBehavior, @@ -34,7 +35,7 @@ import { latestVersions } from '../utility/latest-versions'; import { relativePathToWorkspaceRoot } from '../utility/paths'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders, ProjectType } from '../utility/workspace-models'; -import { Schema as ApplicationOptions, Style } from './schema'; +import { Schema as ApplicationOptions, Style, TestRunner } from './schema'; const APPLICATION_DEV_DEPENDENCIES = [ { name: '@angular/compiler-cli', version: latestVersions.Angular }, @@ -68,6 +69,7 @@ export default function (options: ApplicationOptions): Rule { await getAppOptions(host, options); const suffix = options.fileNameStyleGuide === '2016' ? '.component' : ''; + const typeSeparator = options.fileNameStyleGuide === '2016' ? '.' : '-'; return chain([ addAppToWorkspaceFile(options, appDir), @@ -85,7 +87,7 @@ export default function (options: ApplicationOptions): Rule { routingScope: 'Root', path: sourceDir, project: options.name, - typeSeparator: undefined, + typeSeparator, }), schematic('component', { name: 'app', @@ -111,6 +113,7 @@ export default function (options: ApplicationOptions): Rule { appName: options.name, folderName, suffix, + typeSeparator, }), move(appDir), ]), @@ -185,62 +188,7 @@ function addDependenciesToPackageJson(options: ApplicationOptions): Rule { } if (!options.skipTests) { - if (options.testRunner === 'vitest') { - rules.push( - addDependency('vitest', latestVersions['vitest'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency('jsdom', latestVersions['jsdom'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - ); - } else { - rules.push( - addDependency('karma', latestVersions['karma'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency('karma-chrome-launcher', latestVersions['karma-chrome-launcher'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency('karma-coverage', latestVersions['karma-coverage'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency('karma-jasmine', latestVersions['karma-jasmine'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency( - 'karma-jasmine-html-reporter', - latestVersions['karma-jasmine-html-reporter'], - { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }, - ), - addDependency('jasmine-core', latestVersions['jasmine-core'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - addDependency('@types/jasmine', latestVersions['@types/jasmine'], { - type: DependencyType.Dev, - existing: ExistingBehavior.Skip, - install: options.skipInstall ? InstallBehavior.None : InstallBehavior.Auto, - }), - ); - } + rules.push(...addTestRunnerDependencies(options.testRunner, !!options.skipInstall)); } return chain(rules); @@ -390,21 +338,15 @@ function addAppToWorkspaceFile(options: ApplicationOptions, appDir: string): Rul test: options.skipTests || options.minimal ? undefined - : options.testRunner === 'vitest' - ? { - builder: Builders.BuildUnitTest, - options: {}, - } - : { - builder: Builders.BuildKarma, - options: { - polyfills: options.zoneless ? undefined : ['zone.js', 'zone.js/testing'], - tsConfig: `${projectRoot}tsconfig.spec.json`, - inlineStyleLanguage, - assets: [{ 'glob': '**/*', 'input': `${projectRoot}public` }], - styles: [`${sourceRoot}/styles.${options.style}`], - }, - }, + : { + builder: Builders.BuildUnitTest, + options: + options.testRunner === TestRunner.Vitest + ? {} + : { + runner: 'karma', + }, + }, }, }; diff --git a/packages/schematics/angular/application/index_spec.ts b/packages/schematics/angular/application/index_spec.ts index 863d0d021cb6..c2f91d110f27 100644 --- a/packages/schematics/angular/application/index_spec.ts +++ b/packages/schematics/angular/application/index_spec.ts @@ -10,7 +10,7 @@ import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/te import { parse as parseJson } from 'jsonc-parser'; import { latestVersions } from '../utility/latest-versions'; import { Schema as WorkspaceOptions } from '../workspace/schema'; -import { Schema as ApplicationOptions, Style, ViewEncapsulation } from './schema'; +import { Schema as ApplicationOptions, Style, TestRunner, ViewEncapsulation } from './schema'; // eslint-disable-next-line @typescript-eslint/no-explicit-any function readJsonFile(tree: UnitTestTree, path: string): any { @@ -442,16 +442,17 @@ describe('Application Schematic', () => { }); it('should set values in angular.json correctly when testRunner is karma', async () => { - const options = { ...defaultOptions, projectRoot: '', testRunner: 'karma' as const }; + const options = { ...defaultOptions, projectRoot: '', testRunner: TestRunner.Karma }; const tree = await schematicRunner.runSchematic('application', options, workspaceTree); const config = JSON.parse(tree.readContent('/angular.json')); const prj = config.projects.foo; const testOpt = prj.architect.test; - expect(testOpt.builder).toEqual('@angular/build:karma'); - expect(testOpt.options.tsConfig).toEqual('tsconfig.spec.json'); - expect(testOpt.options.assets).toEqual([{ glob: '**/*', input: 'public' }]); - expect(testOpt.options.styles).toEqual(['src/styles.css']); + expect(testOpt.builder).toEqual('@angular/build:unit-test'); + expect(testOpt.options.runner).toEqual('karma'); + expect(testOpt.options.tsConfig).toBeUndefined(); + expect(testOpt.options.assets).toBeUndefined(); + expect(testOpt.options.styles).toBeUndefined(); }); it('should set the relative tsconfig paths', async () => { @@ -869,4 +870,75 @@ describe('Application Schematic', () => { const stylesContent = tree.readContent('/projects/foo/src/styles.css'); expect(stylesContent).toContain('@import "tailwindcss";'); }); + + describe(`fileNameStyleGuide: '2016'`, () => { + it('should create a component with the correct template and style urls', async () => { + const options = { ...defaultOptions, fileNameStyleGuide: '2016' as const }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const component = tree.readContent('/projects/foo/src/app/app.component.ts'); + const main = tree.readContent('/projects/foo/src/main.ts'); + expect(component).toContain(`templateUrl: './app.component.html'`); + expect(component).toContain(`styleUrl: './app.component.css'`); + expect(main).toContain(`import { App } from './app/app.component'`); + }); + + it('should create a test file with import from the path without suffix', async () => { + const options = { ...defaultOptions, fileNameStyleGuide: '2016' as const }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const componentSpec = tree.readContent('/projects/foo/src/app/app.component.spec.ts'); + expect(componentSpec).toContain(`import { App } from './app.component'`); + }); + + describe('standalone: false', () => { + it('should create a component with the correct template and style urls', async () => { + const options = { + ...defaultOptions, + standalone: false, + fileNameStyleGuide: '2016' as const, + }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const component = tree.readContent('/projects/foo/src/app/app.component.ts'); + expect(component).toContain(`templateUrl: './app.component.html'`); + expect(component).toContain(`styleUrl: './app.component.css'`); + }); + + it('should create a test file with import from the path without suffix', async () => { + const options = { + ...defaultOptions, + standalone: false, + fileNameStyleGuide: '2016' as const, + }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const componentSpec = tree.readContent('/projects/foo/src/app/app.component.spec.ts'); + expect(componentSpec).toContain(`import { App } from './app.component'`); + }); + + it('should create a module with the correct suffix', async () => { + const options = { + ...defaultOptions, + standalone: false, + fileNameStyleGuide: '2016' as const, + }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const module = tree.readContent('/projects/foo/src/app/app.module.ts'); + const main = tree.readContent('/projects/foo/src/main.ts'); + expect(module).toContain(`import { App } from './app.component'`); + expect(main).toContain(`import { AppModule } from './app/app.module'`); + }); + + it('should create a routing module with the correct suffix', async () => { + const options = { + ...defaultOptions, + standalone: false, + routing: true, + fileNameStyleGuide: '2016' as const, + }; + const tree = await schematicRunner.runSchematic('application', options, workspaceTree); + const module = tree.readContent('/projects/foo/src/app/app.module.ts'); + const routingModule = tree.readContent('/projects/foo/src/app/app-routing.module.ts'); + expect(routingModule).toBeDefined(); + expect(module).toContain(`import { AppRoutingModule } from './app-routing.module'`); + }); + }); + }); }); diff --git a/packages/schematics/angular/collection.json b/packages/schematics/angular/collection.json index 88bd8b2ee326..8d876cf7cb5b 100755 --- a/packages/schematics/angular/collection.json +++ b/packages/schematics/angular/collection.json @@ -144,7 +144,7 @@ "private": true, "description": "[INTERNAL] Adds tailwind to a project. Intended for use for ng new/add." }, - "jasmine-to-vitest": { + "refactor-jasmine-vitest": { "factory": "./refactor/jasmine-vitest", "schema": "./refactor/jasmine-vitest/schema.json", "description": "[EXPERIMENTAL] Refactors Jasmine tests to use Vitest APIs.", diff --git a/packages/schematics/angular/config/files/vitest-base.config.ts.template b/packages/schematics/angular/config/files/vitest-base.config.ts.template new file mode 100644 index 000000000000..1f5a2340af39 --- /dev/null +++ b/packages/schematics/angular/config/files/vitest-base.config.ts.template @@ -0,0 +1,9 @@ +// Learn more about Vitest configuration options at https://vitest.dev/config/ + +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + // ... + }, +}); diff --git a/packages/schematics/angular/config/index.ts b/packages/schematics/angular/config/index.ts index 209ac0de91f9..c4bafc39657e 100644 --- a/packages/schematics/angular/config/index.ts +++ b/packages/schematics/angular/config/index.ts @@ -30,11 +30,71 @@ export default createProjectSchematic((options, { project }) => { return addKarmaConfig(options); case ConfigType.Browserslist: return addBrowserslistConfig(project.root); + case ConfigType.Vitest: + return addVitestConfig(options); default: throw new SchematicsException(`"${options.type}" is an unknown configuration file type.`); } }); +function addVitestConfig(options: ConfigOptions): Rule { + return (tree, context) => + updateWorkspace((workspace) => { + const project = workspace.projects.get(options.project); + if (!project) { + throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`); + } + + const testTarget = project.targets.get('test'); + if (!testTarget) { + throw new SchematicsException( + `No "test" target found for project "${options.project}".` + + ' A "test" target is required to generate a Vitest configuration.', + ); + } + + if (testTarget.builder !== AngularBuilder.BuildUnitTest) { + throw new SchematicsException( + `Cannot add a Vitest configuration as builder for "test" target in project does not` + + ` use "${AngularBuilder.BuildUnitTest}".`, + ); + } + + testTarget.options ??= {}; + testTarget.options.runnerConfig = true; + + // Check runner option. + if (testTarget.options.runner === 'karma') { + context.logger.warn( + `The "test" target is configured to use the "karma" runner in the main options.` + + ' The generated "vitest-base.config.ts" file may not be used.', + ); + } + + for (const [name, config] of Object.entries(testTarget.configurations ?? {})) { + if ( + config && + typeof config === 'object' && + 'runner' in config && + config.runner === 'karma' + ) { + context.logger.warn( + `The "test" target's "${name}" configuration is configured to use the "karma" runner.` + + ' The generated "vitest-base.config.ts" file may not be used for that configuration.', + ); + } + } + + return mergeWith( + apply(url('./files'), [ + filter((p) => p.endsWith('vitest-base.config.ts.template')), + applyTemplates({}), + move(project.root), + ]), + ); + }); +} + async function addBrowserslistConfig(projectRoot: string): Promise { return mergeWith( apply(url('./files'), [ @@ -47,49 +107,88 @@ async function addBrowserslistConfig(projectRoot: string): Promise { } function addKarmaConfig(options: ConfigOptions): Rule { - return updateWorkspace((workspace) => { - const project = workspace.projects.get(options.project); - if (!project) { - throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`); - } - - const testTarget = project.targets.get('test'); - if (!testTarget) { - throw new SchematicsException( - `No "test" target found for project "${options.project}".` + - ' A "test" target is required to generate a karma configuration.', - ); - } - - if ( - testTarget.builder !== AngularBuilder.Karma && - testTarget.builder !== AngularBuilder.BuildKarma - ) { - throw new SchematicsException( - `Cannot add a karma configuration as builder for "test" target in project does not` + - ` use "${AngularBuilder.Karma}" or "${AngularBuilder.BuildKarma}".`, + return (_, context) => + updateWorkspace((workspace) => { + const project = workspace.projects.get(options.project); + if (!project) { + throw new SchematicsException(`Project name "${options.project}" doesn't not exist.`); + } + + const testTarget = project.targets.get('test'); + if (!testTarget) { + throw new SchematicsException( + `No "test" target found for project "${options.project}".` + + ' A "test" target is required to generate a karma configuration.', + ); + } + + if ( + testTarget.builder !== AngularBuilder.Karma && + testTarget.builder !== AngularBuilder.BuildKarma && + testTarget.builder !== AngularBuilder.BuildUnitTest + ) { + throw new SchematicsException( + `Cannot add a karma configuration as builder for "test" target in project does not` + + ` use "${AngularBuilder.Karma}", "${AngularBuilder.BuildKarma}", or ${AngularBuilder.BuildUnitTest}.`, + ); + } + + testTarget.options ??= {}; + if (testTarget.builder !== AngularBuilder.BuildUnitTest) { + testTarget.options.karmaConfig = path.join(project.root, 'karma.conf.js'); + } else { + // `unit-test` uses the `runnerConfig` option which has configuration discovery if enabled + testTarget.options.runnerConfig = true; + + let isKarmaRunnerConfigured = false; + // Check runner option + if (testTarget.options.runner) { + if (testTarget.options.runner === 'karma') { + isKarmaRunnerConfigured = true; + } else { + context.logger.warn( + `The "test" target is configured to use a runner other than "karma" in the main options.` + + ' The generated "karma.conf.js" file may not be used.', + ); + } + } + + for (const [name, config] of Object.entries(testTarget.configurations ?? {})) { + if (config && typeof config === 'object' && 'runner' in config) { + if (config.runner !== 'karma') { + context.logger.warn( + `The "test" target's "${name}" configuration is configured to use a runner other than "karma".` + + ' The generated "karma.conf.js" file may not be used for that configuration.', + ); + } else { + isKarmaRunnerConfigured = true; + } + } + } + + if (!isKarmaRunnerConfigured) { + context.logger.warn( + `The "test" target is not explicitly configured to use the "karma" runner.` + + ' The generated "karma.conf.js" file may not be used as the default runner is "vitest".', + ); + } + } + // If scoped project (i.e. "@foo/bar"), convert dir to "foo/bar". + let folderName = options.project.startsWith('@') ? options.project.slice(1) : options.project; + if (/[A-Z]/.test(folderName)) { + folderName = strings.dasherize(folderName); + } + + return mergeWith( + apply(url('./files'), [ + filter((p) => p.endsWith('karma.conf.js.template')), + applyTemplates({ + relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root), + folderName, + needDevkitPlugin: testTarget.builder === AngularBuilder.Karma, + }), + move(project.root), + ]), ); - } - - testTarget.options ??= {}; - testTarget.options.karmaConfig = path.join(project.root, 'karma.conf.js'); - - // If scoped project (i.e. "@foo/bar"), convert dir to "foo/bar". - let folderName = options.project.startsWith('@') ? options.project.slice(1) : options.project; - if (/[A-Z]/.test(folderName)) { - folderName = strings.dasherize(folderName); - } - - return mergeWith( - apply(url('./files'), [ - filter((p) => p.endsWith('karma.conf.js.template')), - applyTemplates({ - relativePathToWorkspaceRoot: relativePathToWorkspaceRoot(project.root), - folderName, - needDevkitPlugin: testTarget.builder === AngularBuilder.Karma, - }), - move(project.root), - ]), - ); - }); + }); } diff --git a/packages/schematics/angular/config/index_spec.ts b/packages/schematics/angular/config/index_spec.ts index 1bbc08d9e85b..bc1715c4866a 100644 --- a/packages/schematics/angular/config/index_spec.ts +++ b/packages/schematics/angular/config/index_spec.ts @@ -95,6 +95,88 @@ describe('Config Schematic', () => { const { karmaConfig } = prj.architect.test.options; expect(karmaConfig).toBe('projects/foo/karma.conf.js'); }); + + describe('with "unit-test" builder', () => { + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.builder = '@angular/build:unit-test'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + }); + + it(`should not set 'karmaConfig' in test builder`, async () => { + const tree = await runConfigSchematic(ConfigType.Karma); + const config = JSON.parse(tree.readContent('/angular.json')); + const prj = config.projects.foo; + const { karmaConfig } = prj.architect.test.options; + expect(karmaConfig).toBeUndefined(); + }); + + it(`should warn when 'runner' is not specified`, async () => { + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Karma); + expect( + logs.some((v) => v.includes('may not be used as the default runner is "vitest"')), + ).toBeTrue(); + }); + + it(`should warn when 'runner' is 'vitest' in options`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.options ??= {}; + angularJson.projects.foo.architect.test.options.runner = 'vitest'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Karma); + expect( + logs.some((v) => v.includes('runner other than "karma" in the main options')), + ).toBeTrue(); + }); + + it(`should warn when 'runner' is 'vitest' in a configuration`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.configurations ??= {}; + angularJson.projects.foo.architect.test.configurations.ci = { runner: 'vitest' }; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Karma); + expect( + logs.some((v) => v.includes(`"ci" configuration is configured to use a runner`)), + ).toBeTrue(); + }); + + it(`should not warn when 'runner' is 'karma' in options`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.options ??= {}; + angularJson.projects.foo.architect.test.options.runner = 'karma'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Karma); + expect(logs.length).toBe(0); + }); + + it(`should not warn when 'runner' is 'karma' in a configuration`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.configurations ??= {}; + angularJson.projects.foo.architect.test.configurations.ci = { runner: 'karma' }; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Karma); + expect(logs.length).toBe(0); + }); + }); }); describe(`when 'type' is 'browserslist'`, () => { @@ -103,4 +185,78 @@ describe('Config Schematic', () => { expect(tree.exists('projects/foo/.browserslistrc')).toBeTrue(); }); }); + + describe(`when 'type' is 'vitest'`, () => { + beforeEach(() => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.builder = '@angular/build:unit-test'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + }); + + it('should create a vitest-base.config.ts file', async () => { + const tree = await runConfigSchematic(ConfigType.Vitest); + expect(tree.exists('projects/foo/vitest-base.config.ts')).toBeTrue(); + }); + + it(`should set 'runnerConfig' in test builder`, async () => { + const tree = await runConfigSchematic(ConfigType.Vitest); + const config = JSON.parse(tree.readContent('/angular.json')); + const prj = config.projects.foo; + const { runnerConfig } = prj.architect.test.options; + expect(runnerConfig).toBe(true); + }); + + it('should throw an error if the builder is not "unit-test"', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.builder = '@angular/build:karma'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + await expectAsync(runConfigSchematic(ConfigType.Vitest)).toBeRejectedWithError( + /Cannot add a Vitest configuration as builder for "test" target/, + ); + }); + + it(`should warn when 'runner' is 'karma' in options`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.options ??= {}; + angularJson.projects.foo.architect.test.options.runner = 'karma'; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Vitest); + expect( + logs.some((v) => + v.includes( + `The "test" target is configured to use the "karma" runner in the main options.`, + ), + ), + ).toBeTrue(); + }); + + it(`should warn when 'runner' is 'karma' in a configuration`, async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const angularJson = applicationTree.readJson('angular.json') as any; + angularJson.projects.foo.architect.test.configurations ??= {}; + angularJson.projects.foo.architect.test.configurations.ci = { runner: 'karma' }; + applicationTree.overwrite('angular.json', JSON.stringify(angularJson)); + + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Vitest); + expect( + logs.some((v) => v.includes(`"ci" configuration is configured to use the "karma" runner`)), + ).toBeTrue(); + }); + + it(`should not warn when 'runner' is not set`, async () => { + const logs: string[] = []; + schematicRunner.logger.subscribe(({ message }) => logs.push(message)); + await runConfigSchematic(ConfigType.Vitest); + expect(logs.length).toBe(0); + }); + }); }); diff --git a/packages/schematics/angular/config/schema.json b/packages/schematics/angular/config/schema.json index 14bb34f07260..dd755e5ac6ae 100644 --- a/packages/schematics/angular/config/schema.json +++ b/packages/schematics/angular/config/schema.json @@ -16,7 +16,7 @@ "type": { "type": "string", "description": "Specifies the type of configuration file to generate.", - "enum": ["karma", "browserslist"], + "enum": ["karma", "browserslist", "vitest"], "x-prompt": "Which type of configuration file would you like to create?", "$default": { "$source": "argv", diff --git a/packages/schematics/angular/directive/files/__name@dasherize__.__type@dasherize__.ts.template b/packages/schematics/angular/directive/files/__name@dasherize__.__type@dasherize__.ts.template index f6c2ba006be3..624b3cafbec1 100644 --- a/packages/schematics/angular/directive/files/__name@dasherize__.__type@dasherize__.ts.template +++ b/packages/schematics/angular/directive/files/__name@dasherize__.__type@dasherize__.ts.template @@ -1,8 +1,8 @@ import { Directive } from '@angular/core'; @Directive({ - selector: '[<%= selector %>]'<% if(!standalone) {%>, - standalone: false<%}%> + selector: '[<%= selector %>]',<% if(!standalone) {%> + standalone: false,<%}%> }) export class <%= classifiedName %> { diff --git a/packages/schematics/angular/guard/implements-files/__name@dasherize____typeSeparator__guard.ts.template b/packages/schematics/angular/guard/implements-files/__name@dasherize____typeSeparator__guard.ts.template index 918718d3e468..3ce1150879da 100644 --- a/packages/schematics/angular/guard/implements-files/__name@dasherize____typeSeparator__guard.ts.template +++ b/packages/schematics/angular/guard/implements-files/__name@dasherize____typeSeparator__guard.ts.template @@ -2,7 +2,7 @@ import { Injectable } from '@angular/core'; import { <%= routerImports %> } from '@angular/router'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class <%= classify(name) %>Guard implements <%= implementations %> { <% if (implements.includes('CanActivate')) { %>canActivate( diff --git a/packages/schematics/angular/library/files/tsconfig.spec.json.template b/packages/schematics/angular/library/files/tsconfig.spec.json.template index 0cec657c8ae9..b2370befce4f 100644 --- a/packages/schematics/angular/library/files/tsconfig.spec.json.template +++ b/packages/schematics/angular/library/files/tsconfig.spec.json.template @@ -9,6 +9,7 @@ ] }, "include": [ - "src/**/*.ts" + "src/**/*.d.ts", + "src/**/*<% if (standalone) { %>.spec<% } %>.ts" ] } diff --git a/packages/schematics/angular/library/index.ts b/packages/schematics/angular/library/index.ts index b02e35b27758..3069664c02d7 100644 --- a/packages/schematics/angular/library/index.ts +++ b/packages/schematics/angular/library/index.ts @@ -20,6 +20,7 @@ import { url, } from '@angular-devkit/schematics'; import { join } from 'node:path/posix'; +import { addTestRunnerDependencies } from '../utility/dependencies'; import { DependencyType, ExistingBehavior, @@ -32,7 +33,7 @@ import { latestVersions } from '../utility/latest-versions'; import { relativePathToWorkspaceRoot } from '../utility/paths'; import { getWorkspace, updateWorkspace } from '../utility/workspace'; import { Builders, ProjectType } from '../utility/workspace-models'; -import { Schema as LibraryOptions } from './schema'; +import { Schema as LibraryOptions, TestRunner } from './schema'; const LIBRARY_DEV_DEPENDENCIES = [ { name: '@angular/compiler-cli', version: latestVersions.Angular }, @@ -69,7 +70,7 @@ function addTsProjectReference(...paths: string[]) { }; } -function addDependenciesToPackageJson(skipInstall: boolean): Rule { +function addDependenciesToPackageJson({ skipInstall, testRunner }: LibraryOptions): Rule { return chain([ ...LIBRARY_DEV_DEPENDENCIES.map((dependency) => addDependency(dependency.name, dependency.version, { @@ -78,6 +79,7 @@ function addDependenciesToPackageJson(skipInstall: boolean): Rule { install: skipInstall ? InstallBehavior.None : InstallBehavior.Auto, }), ), + ...addTestRunnerDependencies(testRunner, !!skipInstall), addDependency('tslib', latestVersions['tslib'], { type: DependencyType.Default, existing: ExistingBehavior.Skip, @@ -91,7 +93,6 @@ function addLibToWorkspaceFile( projectRoot: string, projectName: string, hasZoneDependency: boolean, - hasVitest: boolean, ): Rule { return updateWorkspace((workspace) => { workspace.projects.add({ @@ -113,20 +114,21 @@ function addLibToWorkspaceFile( }, }, }, - test: hasVitest - ? { - builder: Builders.BuildUnitTest, - options: { - tsConfig: `${projectRoot}/tsconfig.spec.json`, + test: + options.testRunner === TestRunner.Vitest + ? { + builder: Builders.BuildUnitTest, + options: { + tsConfig: `${projectRoot}/tsconfig.spec.json`, + }, + } + : { + builder: Builders.BuildKarma, + options: { + tsConfig: `${projectRoot}/tsconfig.spec.json`, + polyfills: hasZoneDependency ? ['zone.js', 'zone.js/testing'] : undefined, + }, }, - } - : { - builder: Builders.BuildKarma, - options: { - tsConfig: `${projectRoot}/tsconfig.spec.json`, - polyfills: hasZoneDependency ? ['zone.js', 'zone.js/testing'] : undefined, - }, - }, }, }); }); @@ -158,7 +160,6 @@ export default function (options: LibraryOptions): Rule { const distRoot = `dist/${folderName}`; const sourceDir = `${libDir}/src/lib`; - const hasVitest = getDependency(host, 'vitest') !== null; const templateSource = apply(url('./files'), [ applyTemplates({ @@ -172,7 +173,7 @@ export default function (options: LibraryOptions): Rule { angularLatestVersion: latestVersions.Angular.replace(/~|\^/, ''), tsLibLatestVersion: latestVersions['tslib'].replace(/~|\^/, ''), folderName, - testTypesPackage: hasVitest ? 'vitest/globals' : 'jasmine', + testTypesPackage: options.testRunner === TestRunner.Vitest ? 'vitest/globals' : 'jasmine', }), move(libDir), ]); @@ -181,8 +182,8 @@ export default function (options: LibraryOptions): Rule { return chain([ mergeWith(templateSource), - addLibToWorkspaceFile(options, libDir, packageName, hasZoneDependency, hasVitest), - options.skipPackageJson ? noop() : addDependenciesToPackageJson(!!options.skipInstall), + addLibToWorkspaceFile(options, libDir, packageName, hasZoneDependency), + options.skipPackageJson ? noop() : addDependenciesToPackageJson(options), options.skipTsConfig ? noop() : updateTsConfig(packageName, './' + distRoot), options.skipTsConfig ? noop() diff --git a/packages/schematics/angular/library/index_spec.ts b/packages/schematics/angular/library/index_spec.ts index 319abbaa5162..bf4f8714294e 100644 --- a/packages/schematics/angular/library/index_spec.ts +++ b/packages/schematics/angular/library/index_spec.ts @@ -407,11 +407,11 @@ describe('Library Schematic', () => { expect(workspace.projects.foo.architect.build.builder).toBe('@angular/build:ng-packagr'); }); - it(`should add 'karma' test builder`, async () => { + it(`should add 'unit-test' test builder`, async () => { const tree = await schematicRunner.runSchematic('library', defaultOptions, workspaceTree); const workspace = JSON.parse(tree.readContent('/angular.json')); - expect(workspace.projects.foo.architect.test.builder).toBe('@angular/build:karma'); + expect(workspace.projects.foo.architect.test.builder).toBe('@angular/build:unit-test'); }); it(`should add 'unit-test' test builder`, async () => { diff --git a/packages/schematics/angular/library/schema.json b/packages/schematics/angular/library/schema.json index 62ffdbb422a0..bb3d227e5245 100644 --- a/packages/schematics/angular/library/schema.json +++ b/packages/schematics/angular/library/schema.json @@ -53,6 +53,12 @@ "type": "boolean", "default": true, "x-user-analytics": "ep.ng_standalone" + }, + "testRunner": { + "description": "The unit testing runner to use.", + "type": "string", + "enum": ["vitest", "karma"], + "default": "vitest" } }, "required": ["name"] diff --git a/packages/schematics/angular/migrations/use-application-builder/migration.ts b/packages/schematics/angular/migrations/use-application-builder/migration.ts index cc858b59de54..b481c4f30034 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration.ts @@ -174,7 +174,11 @@ function updateProjects(tree: Tree, context: SchematicContext) { } const buildTarget = project.targets.get('build'); - if (!buildTarget || buildTarget.builder === Builders.Application) { + if ( + !buildTarget || + buildTarget.builder === Builders.Application || + buildTarget.builder === Builders.BuildApplication + ) { continue; } @@ -362,10 +366,16 @@ export default function (): Rule { ), // Update main tsconfig updateJsonFile('tsconfig.json', (rootJson) => { - rootJson.modify(['compilerOptions', 'esModuleInterop'], true); + const module = rootJson.get(['compilerOptions', 'module']); + const hasPreserveModule = typeof module === 'string' && module.toLowerCase() === 'preserve'; + + if (!hasPreserveModule) { + rootJson.modify(['compilerOptions', 'esModuleInterop'], true); + rootJson.modify(['compilerOptions', 'moduleResolution'], 'bundler'); + } + rootJson.modify(['compilerOptions', 'downlevelIteration'], undefined); rootJson.modify(['compilerOptions', 'allowSyntheticDefaultImports'], undefined); - rootJson.modify(['compilerOptions', 'moduleResolution'], 'bundler'); }), ]); } diff --git a/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts b/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts index a8d50193958e..fd10352c6eac 100644 --- a/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts +++ b/packages/schematics/angular/migrations/use-application-builder/migration_spec.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.dev/license */ +import { JsonObject } from '@angular-devkit/core'; import { EmptyTree } from '@angular-devkit/schematics'; import { SchematicTestRunner, UnitTestTree } from '@angular-devkit/schematics/testing'; import { Builders, ProjectType, WorkspaceSchema } from '../../utility/workspace-models'; @@ -448,4 +449,16 @@ describe(`Migration to use the application builder`, () => { expect(devDependencies['postcss']).toBeUndefined(); }); + + it('it should not add esModuleInterop and moduleResolution when module is preserve', async () => { + tree.overwrite( + 'tsconfig.json', + JSON.stringify({ + compilerOptions: { module: 'preserve' }, + }), + ); + const newTree = await schematicRunner.runSchematic(schematicName, {}, tree); + const { compilerOptions } = newTree.readJson('tsconfig.json') as JsonObject; + expect(compilerOptions).toEqual({ module: 'preserve' }); + }); }); diff --git a/packages/schematics/angular/ng-new/index.ts b/packages/schematics/angular/ng-new/index.ts index 7fca64d69ce3..856343e82b8f 100644 --- a/packages/schematics/angular/ng-new/index.ts +++ b/packages/schematics/angular/ng-new/index.ts @@ -25,7 +25,7 @@ import { import { Schema as ApplicationOptions } from '../application/schema'; import { JSONFile } from '../utility/json-file'; import { Schema as WorkspaceOptions } from '../workspace/schema'; -import { Schema as NgNewOptions } from './schema'; +import { Schema as NgNewOptions, TestRunner } from './schema'; export default function (options: NgNewOptions): Rule { if (!options.directory) { @@ -67,16 +67,21 @@ export default function (options: NgNewOptions): Rule { mergeWith( apply(empty(), [ schematic('workspace', workspaceOptions), - options.createApplication ? schematic('application', applicationOptions) : noop, - schematic('ai-config', { - tool: options.aiConfig?.length ? options.aiConfig : undefined, - }), (tree: Tree) => { - if (options.testRunner === 'karma') { + if (options.testRunner === TestRunner.Karma) { const file = new JSONFile(tree, 'angular.json'); - file.modify(['schematics', '@schematics/angular:application', 'testRunner'], 'karma'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const schematics = file.get(['schematics']) ?? ({} as any); + (schematics['@schematics/angular:application'] ??= {}).testRunner = TestRunner.Karma; + (schematics['@schematics/angular:library'] ??= {}).testRunner = TestRunner.Karma; + + file.modify(['schematics'], schematics); } }, + options.createApplication ? schematic('application', applicationOptions) : noop, + schematic('ai-config', { + tool: options.aiConfig?.length ? options.aiConfig : undefined, + }), move(options.directory), ]), ), diff --git a/packages/schematics/angular/ng-new/index_spec.ts b/packages/schematics/angular/ng-new/index_spec.ts index 20306b875a9a..28e1c13f315b 100644 --- a/packages/schematics/angular/ng-new/index_spec.ts +++ b/packages/schematics/angular/ng-new/index_spec.ts @@ -7,7 +7,7 @@ */ import { SchematicTestRunner } from '@angular-devkit/schematics/testing'; -import { Schema as NgNewOptions } from './schema'; +import { Schema as NgNewOptions, TestRunner } from './schema'; describe('Ng New Schematic', () => { const schematicRunner = new SchematicTestRunner( @@ -159,7 +159,7 @@ describe('Ng New Schematic', () => { }); it(`should set 'testRunner' to 'karma'`, async () => { - const options = { ...defaultOptions, testRunner: 'karma' as const }; + const options = { ...defaultOptions, testRunner: TestRunner.Karma }; const tree = await schematicRunner.runSchematic('ng-new', options); const { @@ -169,7 +169,8 @@ describe('Ng New Schematic', () => { }, }, } = JSON.parse(tree.readContent('/bar/angular.json')); - expect(test.builder).toBe('@angular/build:karma'); + expect(test.builder).toBe('@angular/build:unit-test'); + expect(test.options).toEqual({ runner: 'karma' }); const { devDependencies } = JSON.parse(tree.readContent('/bar/package.json')); expect(devDependencies['karma']).toBeDefined(); @@ -177,11 +178,12 @@ describe('Ng New Schematic', () => { }); it(`should set 'testRunner' to 'karma' in workspace schematic options`, async () => { - const options = { ...defaultOptions, testRunner: 'karma' as const }; + const options = { ...defaultOptions, testRunner: TestRunner.Karma }; const tree = await schematicRunner.runSchematic('ng-new', options); const { schematics } = JSON.parse(tree.readContent('/bar/angular.json')); expect(schematics['@schematics/angular:application'].testRunner).toBe('karma'); + expect(schematics['@schematics/angular:library'].testRunner).toBe('karma'); }); it(`should not add type to class name when file name style guide is '2016'`, async () => { diff --git a/packages/schematics/angular/pipe/files/__name@dasherize____typeSeparator__pipe.ts.template b/packages/schematics/angular/pipe/files/__name@dasherize____typeSeparator__pipe.ts.template index 2e917b4b0503..57765121531e 100644 --- a/packages/schematics/angular/pipe/files/__name@dasherize____typeSeparator__pipe.ts.template +++ b/packages/schematics/angular/pipe/files/__name@dasherize____typeSeparator__pipe.ts.template @@ -1,8 +1,8 @@ import { Pipe, PipeTransform } from '@angular/core'; @Pipe({ - name: '<%= camelize(name) %>'<% if(!standalone) {%>, - standalone: false<%}%> + name: '<%= camelize(name) %>',<% if(!standalone) {%> + standalone: false,<%}%> }) export class <%= classify(name) %>Pipe implements PipeTransform { diff --git a/packages/schematics/angular/refactor/jasmine-vitest/index.ts b/packages/schematics/angular/refactor/jasmine-vitest/index.ts index fb545b8bcbca..a7e34ad26c02 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/index.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/index.ts @@ -119,7 +119,9 @@ export default function (options: Schema): Rule { for (const file of files) { reporter.incrementScannedFiles(); const content = tree.readText(file); - const newContent = transformJasmineToVitest(file, content, reporter); + const newContent = transformJasmineToVitest(file, content, reporter, { + addImports: !!options.addImports, + }); if (content !== newContent) { tree.overwrite(file, newContent); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/index_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/index_spec.ts index 194c4be4298d..fcb804886286 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/index_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/index_spec.ts @@ -53,7 +53,7 @@ describe('Jasmine to Vitest Schematic', () => { appTree.overwrite(specFilePath, content); const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar' }, appTree, ); @@ -84,7 +84,7 @@ describe('Jasmine to Vitest Schematic', () => { appTree.create(testFilePath, testFileContent); const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', fileSuffix: '.test.ts' }, appTree, ); @@ -113,7 +113,7 @@ describe('Jasmine to Vitest Schematic', () => { schematicRunner.logger.subscribe((entry) => logs.push(entry.message)); await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', verbose: true }, appTree, ); @@ -138,7 +138,7 @@ describe('Jasmine to Vitest Schematic', () => { it('should only transform the specified file', async () => { const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', include: 'src/app/nested/nested.spec.ts' }, appTree, ); @@ -152,7 +152,7 @@ describe('Jasmine to Vitest Schematic', () => { it('should handle a Windows-style path', async () => { const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', include: 'src\\app\\nested\\nested.spec.ts' }, appTree, ); @@ -171,7 +171,7 @@ describe('Jasmine to Vitest Schematic', () => { ); const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', include: 'src/app' }, appTree, ); @@ -188,7 +188,7 @@ describe('Jasmine to Vitest Schematic', () => { it('should process all files if `include` is not provided', async () => { const tree = await schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar' }, appTree, ); @@ -203,7 +203,7 @@ describe('Jasmine to Vitest Schematic', () => { it('should throw if the include path does not exist', async () => { await expectAsync( schematicRunner.runSchematic( - 'jasmine-to-vitest', + 'refactor-jasmine-vitest', { project: 'bar', include: 'src/non-existent' }, appTree, ), @@ -226,7 +226,7 @@ describe('Jasmine to Vitest Schematic', () => { const logs: string[] = []; schematicRunner.logger.subscribe((entry) => logs.push(entry.message)); - await schematicRunner.runSchematic('jasmine-to-vitest', {}, appTree); + await schematicRunner.runSchematic('refactor-jasmine-vitest', {}, appTree); expect(logs).toContain('Jasmine to Vitest Refactoring Summary:'); expect(logs).toContain('- 1 test file(s) scanned.'); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/schema.json b/packages/schematics/angular/refactor/jasmine-vitest/schema.json index 5756b1f68ce9..99f34057ffb5 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/schema.json +++ b/packages/schematics/angular/refactor/jasmine-vitest/schema.json @@ -25,6 +25,11 @@ "type": "boolean", "description": "Enable verbose logging to see detailed information about the transformations being applied.", "default": false + }, + "addImports": { + "type": "boolean", + "description": "Whether to add imports for the Vitest API. The Angular `unit-test` system automatically uses the Vitest globals option, which means explicit imports for global APIs like `describe`, `it`, `expect`, and `vi` are often not strictly necessary unless Vitest has been configured not to use globals.", + "default": false } } } diff --git a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.integration_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.integration_spec.ts index 60e2675b7572..8944e34dfa14 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.integration_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.integration_spec.ts @@ -14,7 +14,7 @@ import { RefactorReporter } from './utils/refactor-reporter'; async function expectTransformation(input: string, expected: string): Promise { const logger = new logging.NullLogger(); const reporter = new RefactorReporter(logger); - const transformed = transformJasmineToVitest('spec.ts', input, reporter); + const transformed = transformJasmineToVitest('spec.ts', input, reporter, { addImports: false }); const formattedTransformed = await format(transformed, { parser: 'typescript' }); const formattedExpected = await format(expected, { parser: 'typescript' }); @@ -255,14 +255,15 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { }); `; + /* eslint-disable max-len */ const vitestCode = ` describe('Complex Scenarios', () => { let serviceMock; beforeEach(() => { serviceMock = { - getData: vi.fn().mockReturnValue(expect.any(String)), - process: vi.fn().mockReturnValue(undefined), + getData: vi.fn().mockName("MyService.getData").mockReturnValue(expect.any(String)), + process: vi.fn().mockName("MyService.process").mockReturnValue(undefined), }; }); @@ -274,6 +275,7 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { it('should handle array contents checking', () => { const arr = [1, 2, 3]; + // TODO: vitest-migration: Verify this matches strict array content (multiset equality). Vitest's arrayContaining is a subset check. expect(arr).toHaveLength(3); expect(arr).toEqual(expect.arrayContaining([3, 2, 1])); }); @@ -299,6 +301,7 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => { }); }); `; + /* eslint-enable max-len */ await expectTransformation(jasmineCode, vitestCode); }); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts index 36fa9a856f37..79f4ee2cfc9a 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts @@ -6,6 +6,13 @@ * found in the LICENSE file at https://angular.dev/license */ +/** + * @fileoverview This is the main entry point for the Jasmine to Vitest transformation. + * It orchestrates the application of various AST transformers to convert Jasmine test + * syntax and APIs to their Vitest equivalents. It also handles import management, + * blank line preservation, and reporting of transformation details. + */ + import ts from '../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; import { transformDoneCallback, @@ -39,12 +46,35 @@ import { transformSpyReset, } from './transformers/jasmine-spy'; import { transformJasmineTypes } from './transformers/jasmine-type'; -import { getVitestAutoImports } from './utils/ast-helpers'; +import { addVitestValueImport, getVitestAutoImports } from './utils/ast-helpers'; import { RefactorContext } from './utils/refactor-context'; import { RefactorReporter } from './utils/refactor-reporter'; +/** + * A placeholder used to temporarily replace blank lines in the source code. + * This is necessary because TypeScript's printer removes blank lines by default. + */ const BLANK_LINE_PLACEHOLDER = '// __PRESERVE_BLANK_LINE__'; +/** + * Vitest function names that should be imported when using the --add-imports option. + */ +const VITEST_FUNCTION_NAMES = new Set([ + 'describe', + 'it', + 'expect', + 'beforeEach', + 'afterEach', + 'beforeAll', + 'afterAll', +]); + +/** + * Replaces blank lines in the content with a placeholder to prevent TypeScript's printer + * from removing them. This ensures that the original formatting of blank lines is preserved. + * @param content The source code content. + * @returns The content with blank lines replaced by placeholders. + */ function preserveBlankLines(content: string): string { return content .split('\n') @@ -52,23 +82,90 @@ function preserveBlankLines(content: string): string { .join('\n'); } +/** + * Restores blank lines in the content by replacing the placeholder with actual blank lines. + * This is called after TypeScript's printer has processed the file. + * @param content The content with blank line placeholders. + * @returns The content with blank lines restored. + */ function restoreBlankLines(content: string): string { - const regex = new RegExp(`^\\s*${BLANK_LINE_PLACEHOLDER.replace(/\//g, '\\/')}\\s*$`, 'gm'); + const regex = /^\s*\/\/ __PRESERVE_BLANK_LINE__\s*$/gm; return content.replace(regex, ''); } +/** + * A collection of transformers that operate on `ts.CallExpression` nodes. + * These are applied in stages to ensure correct order of operations: + * 1. High-Level & Context-Sensitive: Transformations that fundamentally change the call. + * 2. Core Matcher & Spy: Bulk conversions for `expect(...)` and `spyOn(...)`. + * 3. Global Functions & Cleanup: Handles global Jasmine functions and unsupported APIs. + */ +const callExpressionTransformers = [ + // **Stage 1: High-Level & Context-Sensitive Transformations** + // These transformers often wrap or fundamentally change the nature of the call, + // so they need to run before more specific matchers. + transformWithContext, + transformExpectAsync, + transformFocusedAndSkippedTests, + transformPending, + transformDoneCallback, + + // **Stage 2: Core Matcher & Spy Transformations** + // This is the bulk of the `expect(...)` and `spyOn(...)` conversions. + transformSyntacticSugarMatchers, + transformComplexMatchers, + transformSpies, + transformCreateSpyObj, + transformSpyReset, + transformSpyCallInspection, + transformtoHaveBeenCalledBefore, + transformToHaveClass, + + // **Stage 3: Global Functions & Cleanup** + // These handle global Jasmine functions and catch-alls for unsupported APIs. + transformTimerMocks, + transformGlobalFunctions, + transformUnsupportedJasmineCalls, +]; + +/** + * A collection of transformers that operate on `ts.PropertyAccessExpression` nodes. + * These primarily handle `jasmine.any()` and other `jasmine.*` properties. + */ +const propertyAccessExpressionTransformers = [ + // These transformers handle `jasmine.any()` and other `jasmine.*` properties. + transformAsymmetricMatchers, + transformSpyCallInspection, + transformUnknownJasmineProperties, +]; + +/** + * A collection of transformers that operate on `ts.ExpressionStatement` nodes. + * These are mutually exclusive; the first one that matches will be applied. + */ +const expressionStatementTransformers = [ + transformCalledOnceWith, + transformArrayWithExactContents, + transformExpectNothing, + transformFail, + transformDefaultTimeoutInterval, +]; + /** * Transforms a string of Jasmine test code to Vitest test code. * This is the main entry point for the transformation. + * @param filePath The path to the file being transformed. * @param content The source code to transform. * @param reporter The reporter to track TODOs. + * @param options Transformation options, including whether to add Vitest API imports. * @returns The transformed code. */ export function transformJasmineToVitest( filePath: string, content: string, reporter: RefactorReporter, + options: { addImports: boolean }, ): string { const contentWithPlaceholders = preserveBlankLines(content); @@ -80,13 +177,16 @@ export function transformJasmineToVitest( ts.ScriptKind.TS, ); - const pendingVitestImports = new Set(); + const pendingVitestValueImports = new Set(); + const pendingVitestTypeImports = new Set(); + const transformer: ts.TransformerFactory = (context) => { const refactorCtx: RefactorContext = { sourceFile, reporter, tsContext: context, - pendingVitestImports, + pendingVitestValueImports, + pendingVitestTypeImports, }; const visitor: ts.Visitor = (node) => { @@ -94,59 +194,24 @@ export function transformJasmineToVitest( // Transform the node itself based on its type if (ts.isCallExpression(transformedNode)) { - const transformations = [ - // **Stage 1: High-Level & Context-Sensitive Transformations** - // These transformers often wrap or fundamentally change the nature of the call, - // so they need to run before more specific matchers. - transformWithContext, - transformExpectAsync, - transformFocusedAndSkippedTests, - transformPending, - transformDoneCallback, - - // **Stage 2: Core Matcher & Spy Transformations** - // This is the bulk of the `expect(...)` and `spyOn(...)` conversions. - transformSyntacticSugarMatchers, - transformComplexMatchers, - transformSpies, - transformCreateSpyObj, - transformSpyReset, - transformSpyCallInspection, - transformtoHaveBeenCalledBefore, - transformToHaveClass, - - // **Stage 3: Global Functions & Cleanup** - // These handle global Jasmine functions and catch-alls for unsupported APIs. - transformTimerMocks, - transformGlobalFunctions, - transformUnsupportedJasmineCalls, - ]; - - for (const transformer of transformations) { + if (options.addImports && ts.isIdentifier(transformedNode.expression)) { + const name = transformedNode.expression.text; + if (VITEST_FUNCTION_NAMES.has(name)) { + addVitestValueImport(pendingVitestValueImports, name); + } + } + + for (const transformer of callExpressionTransformers) { transformedNode = transformer(transformedNode, refactorCtx); } } else if (ts.isPropertyAccessExpression(transformedNode)) { - const transformations = [ - // These transformers handle `jasmine.any()` and other `jasmine.*` properties. - transformAsymmetricMatchers, - transformSpyCallInspection, - transformUnknownJasmineProperties, - ]; - for (const transformer of transformations) { + for (const transformer of propertyAccessExpressionTransformers) { transformedNode = transformer(transformedNode, refactorCtx); } } else if (ts.isExpressionStatement(transformedNode)) { // Statement-level transformers are mutually exclusive. The first one that // matches will be applied, and then the visitor will stop for this node. - const statementTransformers = [ - transformCalledOnceWith, - transformArrayWithExactContents, - transformExpectNothing, - transformFail, - transformDefaultTimeoutInterval, - ]; - - for (const transformer of statementTransformers) { + for (const transformer of expressionStatementTransformers) { const result = transformer(transformedNode, refactorCtx); if (result !== transformedNode) { transformedNode = result; @@ -171,16 +236,29 @@ export function transformJasmineToVitest( const result = ts.transform(sourceFile, [transformer]); let transformedSourceFile = result.transformed[0]; - if (transformedSourceFile === sourceFile && !reporter.hasTodos && !pendingVitestImports.size) { + const hasPendingValueImports = pendingVitestValueImports.size > 0; + const hasPendingTypeImports = pendingVitestTypeImports.size > 0; + + if ( + transformedSourceFile === sourceFile && + !reporter.hasTodos && + !hasPendingValueImports && + !hasPendingTypeImports + ) { return content; } - const vitestImport = getVitestAutoImports(pendingVitestImports); - if (vitestImport) { - transformedSourceFile = ts.factory.updateSourceFile(transformedSourceFile, [ - vitestImport, - ...transformedSourceFile.statements, - ]); + if (hasPendingTypeImports || (options.addImports && hasPendingValueImports)) { + const vitestImport = getVitestAutoImports( + options.addImports ? pendingVitestValueImports : new Set(), + pendingVitestTypeImports, + ); + if (vitestImport) { + transformedSourceFile = ts.factory.updateSourceFile(transformedSourceFile, [ + vitestImport, + ...transformedSourceFile.statements, + ]); + } } const printer = ts.createPrinter(); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_add-imports_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_add-imports_spec.ts new file mode 100644 index 000000000000..1fd4beb6546e --- /dev/null +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_add-imports_spec.ts @@ -0,0 +1,121 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.dev/license + */ + +import { expectTransformation } from './test-helpers'; + +describe('Jasmine to Vitest Transformer', () => { + describe('addImports option', () => { + it('should add value imports when addImports is true', async () => { + const input = `spyOn(foo, 'bar');`; + const expected = ` + import { vi } from 'vitest'; + vi.spyOn(foo, 'bar'); + `; + await expectTransformation(input, expected, true); + }); + + it('should generate a single, combined import for value and type imports when addImports is true', async () => { + const input = ` + let mySpy: jasmine.Spy; + spyOn(foo, 'bar'); + `; + const expected = ` + import { type Mock, vi } from 'vitest'; + + let mySpy: Mock; + vi.spyOn(foo, 'bar'); + `; + await expectTransformation(input, expected, true); + }); + + it('should only add type imports when addImports is false', async () => { + const input = ` + let mySpy: jasmine.Spy; + spyOn(foo, 'bar'); + `; + const expected = ` + import type { Mock } from 'vitest'; + + let mySpy: Mock; + vi.spyOn(foo, 'bar'); + `; + await expectTransformation(input, expected, false); + }); + + it('should not add an import if no Vitest APIs are used, even when addImports is true', async () => { + const input = `const a = 1;`; + const expected = `const a = 1;`; + await expectTransformation(input, expected, true); + }); + + it('should add imports for top-level describe and it when addImports is true', async () => { + const input = ` + describe('My Suite', () => { + it('should do something', () => { + // test content + }); + }); + `; + const expected = ` + import { describe, it } from 'vitest'; + + describe('My Suite', () => { + it('should do something', () => { + // test content + }); + }); + `; + await expectTransformation(input, expected, true); + }); + + it('should add imports for top-level expect when addImports is true', async () => { + const input = `expect(true).toBe(true);`; + const expected = ` + import { expect } from 'vitest'; + expect(true).toBe(true); + `; + await expectTransformation(input, expected, true); + }); + + it('should add imports for beforeEach and afterEach when addImports is true', async () => { + const input = ` + describe('My Suite', () => { + beforeEach(() => {}); + afterEach(() => {}); + }); + `; + const expected = ` + import { afterEach, beforeEach, describe } from 'vitest'; + + describe('My Suite', () => { + beforeEach(() => {}); + afterEach(() => {}); + }); + `; + await expectTransformation(input, expected, true); + }); + + it('should add imports for beforeAll and afterAll when addImports is true', async () => { + const input = ` + describe('My Suite', () => { + beforeAll(() => {}); + afterAll(() => {}); + }); + `; + const expected = ` + import { afterAll, beforeAll, describe } from 'vitest'; + + describe('My Suite', () => { + beforeAll(() => {}); + afterAll(() => {}); + }); + `; + await expectTransformation(input, expected, true); + }); + }); +}); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_spec.ts index bc55811306b3..abe1f3655cdd 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer_spec.ts @@ -78,17 +78,20 @@ describe('Jasmine to Vitest Transformer', () => { input: `const spy = jasmine.createSpyObj('MyService', { getPromise: Promise.resolve(jasmine.any(String)) });`, expected: ` const spy = { - getPromise: vi.fn().mockReturnValue(Promise.resolve(expect.any(String))), + getPromise: vi.fn().mockName("MyService.getPromise").mockReturnValue(Promise.resolve(expect.any(String))), }; `, }, { description: 'should handle arrayWithExactContents containing nested asymmetric matchers', input: `expect(myArray).toEqual(jasmine.arrayWithExactContents([jasmine.objectContaining({ id: 1 })]));`, + /* eslint-disable max-len */ expected: ` + // TODO: vitest-migration: Verify this matches strict array content (multiset equality). Vitest's arrayContaining is a subset check. expect(myArray).toHaveLength(1); expect(myArray).toEqual(expect.arrayContaining([expect.objectContaining({ id: 1 })])); `, + /* eslint-enable max-len */ }, { description: 'should handle a spy rejecting with an asymmetric matcher', @@ -105,8 +108,8 @@ describe('Jasmine to Vitest Transformer', () => { `, expected: ` const myService = { - methodA: vi.fn(), - propA: 'valueA' + methodA: vi.fn().mockName("MyService.methodA"), + propA: 'valueA', }; vi.spyOn(myService, 'methodA').mockReturnValue('mocked value'); myService.methodA('test'); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts b/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts index bf162192ab2a..a93875b912e9 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/test-helpers.ts @@ -23,10 +23,14 @@ import { RefactorReporter } from './utils/refactor-reporter'; * @param input The Jasmine code snippet to be transformed. * @param expected The expected Vitest code snippet after transformation. */ -export async function expectTransformation(input: string, expected: string): Promise { +export async function expectTransformation( + input: string, + expected: string, + addImports = false, +): Promise { const logger = new logging.NullLogger(); const reporter = new RefactorReporter(logger); - const transformed = transformJasmineToVitest('spec.ts', input, reporter); + const transformed = transformJasmineToVitest('spec.ts', input, reporter, { addImports }); const formattedTransformed = await format(transformed, { parser: 'typescript' }); const formattedExpected = await format(expected, { parser: 'typescript' }); diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts index 235eef129d11..f2b60c35f327 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle.ts @@ -282,6 +282,19 @@ function transformPromiseBasedDone( return undefined; } +function countDoneUsages(node: ts.Node, doneIdentifier: ts.Identifier): number { + let count = 0; + const visitor = (n: ts.Node) => { + if (ts.isIdentifier(n) && n.text === doneIdentifier.text) { + count++; + } + ts.forEachChild(n, visitor); + }; + ts.forEachChild(node, visitor); + + return count; +} + export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContext): ts.Node { const { sourceFile, reporter, tsContext } = refactorCtx; if ( @@ -309,12 +322,17 @@ export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContex return node; } const doneIdentifier = doneParam.name; + + // Count total usages of 'done' in the body + const totalUsages = countDoneUsages(functionArg.body, doneIdentifier); + let handledUsages = 0; let doneWasUsed = false; const bodyVisitor = (bodyNode: ts.Node): ts.Node | ts.Node[] | undefined => { const complexTransformed = transformComplexDoneCallback(bodyNode, doneIdentifier, refactorCtx); if (complexTransformed !== bodyNode) { doneWasUsed = true; + handledUsages++; // complex transform handles one usage return complexTransformed; } @@ -330,6 +348,7 @@ export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContex callExpr.expression.name.text === 'fail' ) { doneWasUsed = true; + handledUsages++; reporter.reportTransformation( sourceFile, bodyNode, @@ -350,6 +369,7 @@ export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContex const promiseTransformed = transformPromiseBasedDone(callExpr, doneIdentifier, refactorCtx); if (promiseTransformed) { doneWasUsed = true; + handledUsages++; return promiseTransformed; } @@ -360,6 +380,7 @@ export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContex callExpr.expression.text === doneIdentifier.text ) { doneWasUsed = true; + handledUsages++; return ts.setTextRange(ts.factory.createEmptyStatement(), callExpr.expression); } @@ -383,6 +404,20 @@ export function transformDoneCallback(node: ts.Node, refactorCtx: RefactorContex return bodyVisitor(node); }); + // Safety check: if we found usages but didn't handle all of them, abort. + if (handledUsages < totalUsages) { + reporter.reportTransformation( + sourceFile, + node, + `Found unhandled usage of \`${doneIdentifier.text}\` callback. Skipping transformation.`, + ); + const category = 'unhandled-done-usage'; + reporter.recordTodo(category); + addTodoComment(node, category); + + return node; + } + if (!doneWasUsed) { return node; } diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle_spec.ts index d5f9e3231180..f41f5fa7ae32 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-lifecycle_spec.ts @@ -132,6 +132,7 @@ describe('Jasmine to Vitest Transformer', () => { }); `, expected: ` + // TODO: vitest-migration: The 'done' callback was used in an unhandled way. Please migrate manually. it('should not transform a function with a parameter that is not a done callback', (value) => { expect(value).toBe(true); }); @@ -157,6 +158,20 @@ describe('Jasmine to Vitest Transformer', () => { }); `, }, + { + description: 'should add a TODO for unhandled done usage', + input: ` + it('should do something with helper', (done) => { + someHelper(done); + }); + `, + expected: ` + // TODO: vitest-migration: The 'done' callback was used in an unhandled way. Please migrate manually. + it('should do something with helper', (done) => { + someHelper(done); + }); + `, + }, ]; testCases.forEach(({ description, input, expected }) => { diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts index 00ea5fa0e775..5de173bcd52b 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts @@ -15,7 +15,11 @@ */ import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; -import { createExpectCallExpression, createPropertyAccess } from '../utils/ast-helpers'; +import { + addVitestValueImport, + createExpectCallExpression, + createPropertyAccess, +} from '../utils/ast-helpers'; import { getJasmineMethodName, isJasmineCallExpression } from '../utils/ast-validation'; import { addTodoComment } from '../utils/comment-helpers'; import { RefactorContext } from '../utils/refactor-context'; @@ -94,7 +98,7 @@ const ASYMMETRIC_MATCHER_NAMES: ReadonlyArray = [ export function transformAsymmetricMatchers( node: ts.Node, - { sourceFile, reporter }: RefactorContext, + { sourceFile, reporter, pendingVitestValueImports }: RefactorContext, ): ts.Node { if ( ts.isPropertyAccessExpression(node) && @@ -103,6 +107,7 @@ export function transformAsymmetricMatchers( ) { const matcherName = node.name.text; if (ASYMMETRIC_MATCHER_NAMES.includes(matcherName)) { + addVitestValueImport(pendingVitestValueImports, 'expect'); reporter.reportTransformation( sourceFile, node, @@ -446,10 +451,14 @@ export function transformArrayWithExactContents( ], ); - return [ - ts.factory.createExpressionStatement(lengthCall), - ts.factory.createExpressionStatement(containingCall), - ]; + const lengthStmt = ts.factory.createExpressionStatement(lengthCall); + const containingStmt = ts.factory.createExpressionStatement(containingCall); + + const category = 'arrayWithExactContents-check'; + reporter.recordTodo(category); + addTodoComment(lengthStmt, category); + + return [lengthStmt, containingStmt]; } export function transformCalledOnceWith( diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts index 69b0637254aa..a46fc63d2a01 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts @@ -225,19 +225,25 @@ expect(mySpyObj).toHaveSpyInteractions();`, { description: 'should transform toEqual(jasmine.arrayWithExactContents()) into two calls', input: `expect(myArray).toEqual(jasmine.arrayWithExactContents(['a', 'b']));`, + /* eslint-disable max-len */ expected: ` + // TODO: vitest-migration: Verify this matches strict array content (multiset equality). Vitest's arrayContaining is a subset check. expect(myArray).toHaveLength(2); expect(myArray).toEqual(expect.arrayContaining(['a', 'b'])); `, + /* eslint-enable max-len */ }, { description: 'should transform toEqual(jasmine.arrayWithExactContents()) with asymmetric matchers', input: `expect(myArray).toEqual(jasmine.arrayWithExactContents([jasmine.any(Number), 'a']));`, + /* eslint-disable max-len */ expected: ` + // TODO: vitest-migration: Verify this matches strict array content (multiset equality). Vitest's arrayContaining is a subset check. expect(myArray).toHaveLength(2); expect(myArray).toEqual(expect.arrayContaining([expect.any(Number), 'a'])); `, + /* eslint-enable max-len */ }, { description: diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts index 1a2c15b2c539..058d015c9773 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc.ts @@ -14,7 +14,7 @@ */ import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; -import { createViCallExpression } from '../utils/ast-helpers'; +import { addVitestValueImport, createViCallExpression } from '../utils/ast-helpers'; import { getJasmineMethodName, isJasmineCallExpression } from '../utils/ast-validation'; import { addTodoComment } from '../utils/comment-helpers'; import { RefactorContext } from '../utils/refactor-context'; @@ -22,7 +22,7 @@ import { TodoCategory } from '../utils/todo-notes'; export function transformTimerMocks( node: ts.Node, - { sourceFile, reporter }: RefactorContext, + { sourceFile, reporter, pendingVitestValueImports }: RefactorContext, ): ts.Node { if ( !ts.isCallExpression(node) || @@ -55,12 +55,21 @@ export function transformTimerMocks( } if (newMethodName) { + addVitestValueImport(pendingVitestValueImports, 'vi'); reporter.reportTransformation( sourceFile, node, `Transformed \`jasmine.clock().${pae.name.text}\` to \`vi.${newMethodName}\`.`, ); - const newArgs = newMethodName === 'useFakeTimers' ? [] : node.arguments; + let newArgs: readonly ts.Expression[] = node.arguments; + if (newMethodName === 'useFakeTimers') { + newArgs = []; + } + if (newMethodName === 'setSystemTime' && node.arguments.length === 0) { + newArgs = [ + ts.factory.createNewExpression(ts.factory.createIdentifier('Date'), undefined, []), + ]; + } return createViCallExpression(newMethodName, newArgs); } @@ -94,7 +103,7 @@ export function transformFail(node: ts.Node, { sourceFile, reporter }: RefactorC export function transformDefaultTimeoutInterval( node: ts.Node, - { sourceFile, reporter }: RefactorContext, + { sourceFile, reporter, pendingVitestValueImports }: RefactorContext, ): ts.Node { if ( ts.isExpressionStatement(node) && @@ -108,6 +117,7 @@ export function transformDefaultTimeoutInterval( assignment.left.expression.text === 'jasmine' && assignment.left.name.text === 'DEFAULT_TIMEOUT_INTERVAL' ) { + addVitestValueImport(pendingVitestValueImports, 'vi'); reporter.reportTransformation( sourceFile, node, diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc_spec.ts index 5095a4448db7..4684f7f69d2d 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-misc_spec.ts @@ -31,6 +31,11 @@ describe('Jasmine to Vitest Transformer', () => { input: `jasmine.clock().mockDate(new Date('2025-01-01'));`, expected: `vi.setSystemTime(new Date('2025-01-01'));`, }, + { + description: 'should transform jasmine.clock().mockDate() to vi.setSystemTime(new Date())', + input: `jasmine.clock().mockDate();`, + expected: `vi.setSystemTime(new Date());`, + }, ]; testCases.forEach(({ description, input, expected }) => { diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts index 759a93301438..39c5802ea1dc 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy.ts @@ -14,13 +14,17 @@ */ import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; -import { createPropertyAccess, createViCallExpression } from '../utils/ast-helpers'; +import { + addVitestValueImport, + createPropertyAccess, + createViCallExpression, +} from '../utils/ast-helpers'; import { getJasmineMethodName, isJasmineCallExpression } from '../utils/ast-validation'; import { addTodoComment } from '../utils/comment-helpers'; import { RefactorContext } from '../utils/refactor-context'; export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts.Node { - const { sourceFile, reporter } = refactorCtx; + const { sourceFile, reporter, pendingVitestValueImports } = refactorCtx; if (!ts.isCallExpression(node)) { return node; } @@ -29,6 +33,7 @@ export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts. ts.isIdentifier(node.expression) && (node.expression.text === 'spyOn' || node.expression.text === 'spyOnProperty') ) { + addVitestValueImport(pendingVitestValueImports, 'vi'); reporter.reportTransformation( sourceFile, node, @@ -181,6 +186,7 @@ export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts. const jasmineMethodName = getJasmineMethodName(node); switch (jasmineMethodName) { case 'createSpy': + addVitestValueImport(pendingVitestValueImports, 'vi'); reporter.reportTransformation( sourceFile, node, @@ -208,19 +214,27 @@ export function transformSpies(node: ts.Node, refactorCtx: RefactorContext): ts. export function transformCreateSpyObj( node: ts.Node, - { sourceFile, reporter }: RefactorContext, + { sourceFile, reporter, pendingVitestValueImports }: RefactorContext, ): ts.Node { if (!isJasmineCallExpression(node, 'createSpyObj')) { return node; } + addVitestValueImport(pendingVitestValueImports, 'vi'); reporter.reportTransformation( sourceFile, node, 'Transformed `jasmine.createSpyObj()` to an object literal with `vi.fn()`.', ); - if (node.arguments.length < 2) { + const firstArg = node.arguments[0]; + const hasBaseName = ts.isStringLiteral(firstArg); + const baseName = hasBaseName ? firstArg.text : undefined; + const methods = hasBaseName ? node.arguments[1] : firstArg; + const propertiesArg = hasBaseName ? node.arguments[2] : node.arguments[1]; + let properties: ts.PropertyAssignment[] = []; + + if (node.arguments.length < 2 && hasBaseName) { const category = 'createSpyObj-single-argument'; reporter.recordTodo(category); addTodoComment(node, category); @@ -228,14 +242,10 @@ export function transformCreateSpyObj( return node; } - const methods = node.arguments[1]; - const propertiesArg = node.arguments[2]; - let properties: ts.PropertyAssignment[] = []; - if (ts.isArrayLiteralExpression(methods)) { - properties = createSpyObjWithArray(methods); + properties = createSpyObjWithArray(methods, baseName); } else if (ts.isObjectLiteralExpression(methods)) { - properties = createSpyObjWithObject(methods); + properties = createSpyObjWithObject(methods, baseName); } else { const category = 'createSpyObj-dynamic-variable'; reporter.recordTodo(category); @@ -257,13 +267,28 @@ export function transformCreateSpyObj( return ts.factory.createObjectLiteralExpression(properties, true); } -function createSpyObjWithArray(methods: ts.ArrayLiteralExpression): ts.PropertyAssignment[] { +function createSpyObjWithArray( + methods: ts.ArrayLiteralExpression, + baseName: string | undefined, +): ts.PropertyAssignment[] { return methods.elements .map((element) => { if (ts.isStringLiteral(element)) { + const mockFn = createViCallExpression('fn'); + const methodName = element.text; + let finalExpression: ts.Expression = mockFn; + + if (baseName) { + finalExpression = ts.factory.createCallExpression( + createPropertyAccess(finalExpression, 'mockName'), + undefined, + [ts.factory.createStringLiteral(`${baseName}.${methodName}`)], + ); + } + return ts.factory.createPropertyAssignment( - ts.factory.createIdentifier(element.text), - createViCallExpression('fn'), + ts.factory.createIdentifier(methodName), + finalExpression, ); } @@ -272,13 +297,25 @@ function createSpyObjWithArray(methods: ts.ArrayLiteralExpression): ts.PropertyA .filter((p): p is ts.PropertyAssignment => !!p); } -function createSpyObjWithObject(methods: ts.ObjectLiteralExpression): ts.PropertyAssignment[] { +function createSpyObjWithObject( + methods: ts.ObjectLiteralExpression, + baseName: string | undefined, +): ts.PropertyAssignment[] { return methods.properties .map((prop) => { if (ts.isPropertyAssignment(prop) && ts.isIdentifier(prop.name)) { const methodName = prop.name.text; const returnValue = prop.initializer; - const mockFn = createViCallExpression('fn'); + let mockFn = createViCallExpression('fn'); + + if (baseName) { + mockFn = ts.factory.createCallExpression( + createPropertyAccess(mockFn, 'mockName'), + undefined, + [ts.factory.createStringLiteral(`${baseName}.${methodName}`)], + ); + } + const mockReturnValue = createPropertyAccess(mockFn, 'mockReturnValue'); return ts.factory.createPropertyAssignment( @@ -328,7 +365,11 @@ function getSpyIdentifierFromCalls(node: ts.PropertyAccessExpression): ts.Expres return undefined; } -function createMockedSpyMockProperty(spyIdentifier: ts.Expression): ts.PropertyAccessExpression { +function createMockedSpyMockProperty( + spyIdentifier: ts.Expression, + pendingVitestValueImports: Set, +): ts.PropertyAccessExpression { + addVitestValueImport(pendingVitestValueImports, 'vi'); const mockedSpy = ts.factory.createCallExpression( createPropertyAccess('vi', 'mocked'), undefined, @@ -338,104 +379,124 @@ function createMockedSpyMockProperty(spyIdentifier: ts.Expression): ts.PropertyA return createPropertyAccess(mockedSpy, 'mock'); } -export function transformSpyCallInspection( +function transformMostRecentArgs( node: ts.Node, - { sourceFile, reporter }: RefactorContext, + { sourceFile, reporter, pendingVitestValueImports }: RefactorContext, ): ts.Node { - // mySpy.calls.mostRecent().args -> vi.mocked(mySpy).mock.lastCall + // Check 1: Is it a property access for `.args`? if ( - ts.isPropertyAccessExpression(node) && - ts.isIdentifier(node.name) && - node.name.text === 'args' + !ts.isPropertyAccessExpression(node) || + !ts.isIdentifier(node.name) || + node.name.text !== 'args' ) { - const mostRecentCall = node.expression; - if ( - ts.isCallExpression(mostRecentCall) && - ts.isPropertyAccessExpression(mostRecentCall.expression) - ) { - const mostRecentPae = mostRecentCall.expression; // mySpy.calls.mostRecent - if ( - ts.isIdentifier(mostRecentPae.name) && - mostRecentPae.name.text === 'mostRecent' && - ts.isPropertyAccessExpression(mostRecentPae.expression) - ) { - const spyIdentifier = getSpyIdentifierFromCalls(mostRecentPae.expression); - if (spyIdentifier) { - reporter.reportTransformation( - sourceFile, - node, - 'Transformed `spy.calls.mostRecent().args` to `vi.mocked(spy).mock.lastCall`.', - ); - const mockProperty = createMockedSpyMockProperty(spyIdentifier); + return node; + } - return createPropertyAccess(mockProperty, 'lastCall'); - } - } - } + // Check 2: Is the preceding expression a call expression? + const mostRecentCall = node.expression; + if ( + !ts.isCallExpression(mostRecentCall) || + !ts.isPropertyAccessExpression(mostRecentCall.expression) + ) { + return node; } - if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) { - const pae = node.expression; // e.g., mySpy.calls.count - const spyIdentifier = ts.isPropertyAccessExpression(pae.expression) - ? getSpyIdentifierFromCalls(pae.expression) - : undefined; - - if (spyIdentifier) { - const mockProperty = createMockedSpyMockProperty(spyIdentifier); - const callsProperty = createPropertyAccess(mockProperty, 'calls'); - - const callName = pae.name.text; - let newExpression: ts.Node | undefined; - let message: string | undefined; - - switch (callName) { - case 'any': - message = 'Transformed `spy.calls.any()` to a check on `mock.calls.length`.'; - newExpression = ts.factory.createBinaryExpression( - createPropertyAccess(callsProperty, 'length'), - ts.SyntaxKind.GreaterThanToken, - ts.factory.createNumericLiteral(0), - ); - break; - case 'count': - message = 'Transformed `spy.calls.count()` to `mock.calls.length`.'; - newExpression = createPropertyAccess(callsProperty, 'length'); - break; - case 'first': - message = 'Transformed `spy.calls.first()` to `mock.calls[0]`.'; - newExpression = ts.factory.createElementAccessExpression(callsProperty, 0); - break; - case 'all': - case 'allArgs': - message = `Transformed \`spy.calls.${callName}()\` to \`mock.calls\`.`; - newExpression = callsProperty; - break; - case 'argsFor': - message = 'Transformed `spy.calls.argsFor()` to `mock.calls[i]`.'; - newExpression = ts.factory.createElementAccessExpression( - callsProperty, - node.arguments[0], - ); - break; - case 'mostRecent': - if ( - !ts.isPropertyAccessExpression(node.parent) || - !ts.isIdentifier(node.parent.name) || - node.parent.name.text !== 'args' - ) { - const category = 'mostRecent-without-args'; - reporter.recordTodo(category); - addTodoComment(node, category); - } + // Check 3: Is it a call to `.mostRecent`? + const mostRecentPae = mostRecentCall.expression; + if ( + !ts.isIdentifier(mostRecentPae.name) || + mostRecentPae.name.text !== 'mostRecent' || + !ts.isPropertyAccessExpression(mostRecentPae.expression) + ) { + return node; + } - return node; - } + // Check 4: Can we get the spy identifier from `spy.calls`? + const spyIdentifier = getSpyIdentifierFromCalls(mostRecentPae.expression); + if (!spyIdentifier) { + return node; + } + + // If all checks pass, perform the transformation. + reporter.reportTransformation( + sourceFile, + node, + 'Transformed `spy.calls.mostRecent().args` to `vi.mocked(spy).mock.lastCall`.', + ); + const mockProperty = createMockedSpyMockProperty(spyIdentifier, pendingVitestValueImports); - if (newExpression && message) { - reporter.reportTransformation(sourceFile, node, message); + return createPropertyAccess(mockProperty, 'lastCall'); +} - return newExpression; - } +export function transformSpyCallInspection(node: ts.Node, refactorCtx: RefactorContext): ts.Node { + const mostRecentArgsTransformed = transformMostRecentArgs(node, refactorCtx); + if (mostRecentArgsTransformed !== node) { + return mostRecentArgsTransformed; + } + + if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) { + return node; + } + + const { sourceFile, reporter, pendingVitestValueImports } = refactorCtx; + + const pae = node.expression; // e.g., mySpy.calls.count + const spyIdentifier = ts.isPropertyAccessExpression(pae.expression) + ? getSpyIdentifierFromCalls(pae.expression) + : undefined; + + if (spyIdentifier) { + const mockProperty = createMockedSpyMockProperty(spyIdentifier, pendingVitestValueImports); + const callsProperty = createPropertyAccess(mockProperty, 'calls'); + + const callName = pae.name.text; + let newExpression: ts.Node | undefined; + let message: string | undefined; + + switch (callName) { + case 'any': + message = 'Transformed `spy.calls.any()` to a check on `mock.calls.length`.'; + newExpression = ts.factory.createBinaryExpression( + createPropertyAccess(callsProperty, 'length'), + ts.SyntaxKind.GreaterThanToken, + ts.factory.createNumericLiteral(0), + ); + break; + case 'count': + message = 'Transformed `spy.calls.count()` to `mock.calls.length`.'; + newExpression = createPropertyAccess(callsProperty, 'length'); + break; + case 'first': + message = 'Transformed `spy.calls.first()` to `mock.calls[0]`.'; + newExpression = ts.factory.createElementAccessExpression(callsProperty, 0); + break; + case 'all': + case 'allArgs': + message = `Transformed \`spy.calls.${callName}()\` to \`mock.calls\`.`; + newExpression = callsProperty; + break; + case 'argsFor': + message = 'Transformed `spy.calls.argsFor()` to `mock.calls[i]`.'; + newExpression = ts.factory.createElementAccessExpression(callsProperty, node.arguments[0]); + break; + case 'mostRecent': + if ( + !ts.isPropertyAccessExpression(node.parent) || + !ts.isIdentifier(node.parent.name) || + node.parent.name.text !== 'args' + ) { + const category = 'mostRecent-without-args'; + reporter.recordTodo(category); + addTodoComment(node, category); + } + + return node; + } + + if (newExpression && message) { + reporter.reportTransformation(sourceFile, node, message); + + return newExpression; } } diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy_spec.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy_spec.ts index c84b35c422ac..06e26e606b5c 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy_spec.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-spy_spec.ts @@ -117,9 +117,18 @@ vi.spyOn(service, 'myMethod').and.unknownStrategy();`, { description: 'should transform jasmine.createSpyObj with an array of methods', input: `const myService = jasmine.createSpyObj('MyService', ['methodA', 'methodB']);`, + expected: `const myService = { + methodA: vi.fn().mockName("MyService.methodA"), + methodB: vi.fn().mockName("MyService.methodB"), + };`, + }, + { + description: + 'should transform jasmine.createSpyObj with an array of methods without base name', + input: `const myService = jasmine.createSpyObj(['methodA', 'methodB']);`, expected: `const myService = { methodA: vi.fn(), - methodB: vi.fn() + methodB: vi.fn(), };`, }, { @@ -133,9 +142,18 @@ vi.spyOn(service, 'myMethod').and.unknownStrategy();`, { description: 'should transform jasmine.createSpyObj with an object of return values', input: `const myService = jasmine.createSpyObj('MyService', { methodA: 'foo', methodB: 42 });`, + expected: `const myService = { + methodA: vi.fn().mockName("MyService.methodA").mockReturnValue('foo'), + methodB: vi.fn().mockName("MyService.methodB").mockReturnValue(42), + };`, + }, + { + description: + 'should transform jasmine.createSpyObj with an object of return values without base name', + input: `const myService = jasmine.createSpyObj({ methodA: 'foo', methodB: 42 });`, expected: `const myService = { methodA: vi.fn().mockReturnValue('foo'), - methodB: vi.fn().mockReturnValue(42) + methodB: vi.fn().mockReturnValue(42), };`, }, { @@ -143,11 +161,11 @@ vi.spyOn(service, 'myMethod').and.unknownStrategy();`, 'should transform jasmine.createSpyObj with an object of return values containing an asymmetric matcher', input: `const myService = jasmine.createSpyObj('MyService', { methodA: jasmine.any(String) });`, expected: `const myService = { - methodA: vi.fn().mockReturnValue(expect.any(String)) + methodA: vi.fn().mockName("MyService.methodA").mockReturnValue(expect.any(String)), };`, }, { - description: 'should add a TODO for jasmine.createSpyObj with only one argument', + description: 'should add a TODO for jasmine.createSpyObj with only base name argument', input: `const myService = jasmine.createSpyObj('MyService');`, expected: ` // TODO: vitest-migration: jasmine.createSpyObj called with a single argument is not supported for transformation. See: https://vitest.dev/api/vi.html#vi-fn @@ -158,23 +176,32 @@ vi.spyOn(service, 'myMethod').and.unknownStrategy();`, description: 'should transform jasmine.createSpyObj with a property map', input: `const myService = jasmine.createSpyObj('MyService', ['methodA'], { propA: 'valueA' });`, expected: `const myService = { - methodA: vi.fn(), - propA: 'valueA' + methodA: vi.fn().mockName("MyService.methodA"), + propA: 'valueA', };`, }, { description: 'should transform jasmine.createSpyObj with a method map and a property map', input: `const myService = jasmine.createSpyObj('MyService', { methodA: 'foo' }, { propA: 'valueA' });`, + expected: `const myService = { + methodA: vi.fn().mockName("MyService.methodA").mockReturnValue('foo'), + propA: 'valueA', + };`, + }, + { + description: + 'should transform jasmine.createSpyObj with a method map and a property map without base name', + input: `const myService = jasmine.createSpyObj({ methodA: 'foo' }, { propA: 'valueA' });`, expected: `const myService = { methodA: vi.fn().mockReturnValue('foo'), - propA: 'valueA' + propA: 'valueA', };`, }, { description: 'should ignore non-string literals in the method array', input: `const myService = jasmine.createSpyObj('MyService', ['methodA', 123, someVar]);`, expected: `const myService = { - methodA: vi.fn() + methodA: vi.fn().mockName("MyService.methodA"), };`, }, ]; diff --git a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-type.ts b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-type.ts index c39674d38045..a7a0c7bedf80 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-type.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-type.ts @@ -14,12 +14,12 @@ */ import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; -import { addVitestAutoImport } from '../utils/ast-helpers'; +import { addVitestTypeImport } from '../utils/ast-helpers'; import { RefactorContext } from '../utils/refactor-context'; export function transformJasmineTypes( node: ts.Node, - { sourceFile, reporter, pendingVitestImports }: RefactorContext, + { sourceFile, reporter, pendingVitestTypeImports }: RefactorContext, ): ts.Node { const typeNameNode = ts.isTypeReferenceNode(node) ? node.typeName : node; if ( @@ -40,7 +40,7 @@ export function transformJasmineTypes( node, `Transformed type \`jasmine.Spy\` to \`${vitestTypeName}\`.`, ); - addVitestAutoImport(pendingVitestImports, vitestTypeName); + addVitestTypeImport(pendingVitestTypeImports, vitestTypeName); return ts.factory.createIdentifier(vitestTypeName); } @@ -51,7 +51,7 @@ export function transformJasmineTypes( node, `Transformed type \`jasmine.SpyObj\` to \`${vitestTypeName}\`.`, ); - addVitestAutoImport(pendingVitestImports, vitestTypeName); + addVitestTypeImport(pendingVitestTypeImports, vitestTypeName); if (ts.isTypeReferenceNode(node)) { return ts.factory.updateTypeReferenceNode( diff --git a/packages/schematics/angular/refactor/jasmine-vitest/utils/ast-helpers.ts b/packages/schematics/angular/refactor/jasmine-vitest/utils/ast-helpers.ts index 5bdb07dd4225..f6f363df1643 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/ast-helpers.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/ast-helpers.ts @@ -8,28 +8,55 @@ import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript'; -export function addVitestAutoImport(imports: Set, importName: string): void { +export function addVitestValueImport(imports: Set, importName: string): void { imports.add(importName); } -export function getVitestAutoImports(imports: Set): ts.ImportDeclaration | undefined { - if (!imports?.size) { +export function addVitestTypeImport(imports: Set, importName: string): void { + imports.add(importName); +} + +export function getVitestAutoImports( + valueImports: Set, + typeImports: Set, +): ts.ImportDeclaration | undefined { + if (valueImports.size === 0 && typeImports.size === 0) { return undefined; } - const importNames = [...imports]; - importNames.sort(); - const importSpecifiers = importNames.map((i) => - ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(i)), + const isClauseTypeOnly = valueImports.size === 0 && typeImports.size > 0; + const allSpecifiers: ts.ImportSpecifier[] = []; + + // Add value imports + for (const i of [...valueImports].sort()) { + allSpecifiers.push( + ts.factory.createImportSpecifier(false, undefined, ts.factory.createIdentifier(i)), + ); + } + + // Add type imports + for (const i of [...typeImports].sort()) { + // Only set isTypeOnly on individual specifiers if the clause itself is NOT type-only + allSpecifiers.push( + ts.factory.createImportSpecifier( + !isClauseTypeOnly, + undefined, + ts.factory.createIdentifier(i), + ), + ); + } + + allSpecifiers.sort((a, b) => a.name.text.localeCompare(b.name.text)); + + const importClause = ts.factory.createImportClause( + isClauseTypeOnly, // Set isTypeOnly on the clause if only type imports + undefined, + ts.factory.createNamedImports(allSpecifiers), ); return ts.factory.createImportDeclaration( undefined, - ts.factory.createImportClause( - ts.SyntaxKind.TypeKeyword, - undefined, - ts.factory.createNamedImports(importSpecifiers), - ), + importClause, ts.factory.createStringLiteral('vitest'), ); } diff --git a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-context.ts b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-context.ts index 3ca9fd3810e7..a7e705a83ce5 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-context.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/refactor-context.ts @@ -23,8 +23,11 @@ export interface RefactorContext { /** The official context from the TypeScript Transformer API. */ readonly tsContext: ts.TransformationContext; + /** A set of Vitest value imports to be added to the file. */ + readonly pendingVitestValueImports: Set; + /** A set of Vitest type imports to be added to the file. */ - readonly pendingVitestImports: Set; + readonly pendingVitestTypeImports: Set; } /** diff --git a/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts b/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts index c0a5eb406e6a..a4964b1e456b 100644 --- a/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts +++ b/packages/schematics/angular/refactor/jasmine-vitest/utils/todo-notes.ts @@ -56,6 +56,10 @@ export const TODO_NOTES = { message: 'Cannot transform jasmine.arrayWithExactContents with a dynamic variable. Please migrate this manually.', }, + 'arrayWithExactContents-check': { + message: + "Verify this matches strict array content (multiset equality). Vitest's arrayContaining is a subset check.", + }, 'expect-nothing': { message: 'expect().nothing() has been removed because it is redundant in Vitest. Tests without assertions pass by default.', @@ -120,6 +124,9 @@ export const TODO_NOTES = { ' Please refactor to access .args directly or use vi.mocked(spy).mock.lastCall.', url: 'https://vitest.dev/api/mocked.html#mock-lastcall', }, + 'unhandled-done-usage': { + message: "The 'done' callback was used in an unhandled way. Please migrate manually.", + }, } as const; /** diff --git a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template index 584a706c6ca1..de24346572c2 100644 --- a/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template +++ b/packages/schematics/angular/service/files/__name@dasherize__.__type@dasherize__.ts.template @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; @Injectable({ - providedIn: 'root' + providedIn: 'root', }) export class <%= classifiedName %> { diff --git a/packages/schematics/angular/service/index_spec.ts b/packages/schematics/angular/service/index_spec.ts index 760cec6b0f7f..56ae5edd2428 100644 --- a/packages/schematics/angular/service/index_spec.ts +++ b/packages/schematics/angular/service/index_spec.ts @@ -55,7 +55,7 @@ describe('Service Schematic', () => { const tree = await schematicRunner.runSchematic('service', options, appTree); const content = tree.readContent('/projects/bar/src/app/foo/foo.ts'); - expect(content).toMatch(/providedIn: 'root'/); + expect(content).toMatch(/providedIn: 'root',/); }); it('should respect the skipTests flag', async () => { diff --git a/packages/schematics/angular/utility/dependencies.ts b/packages/schematics/angular/utility/dependencies.ts index 06c4f38653bd..deb78ca9c0e2 100644 --- a/packages/schematics/angular/utility/dependencies.ts +++ b/packages/schematics/angular/utility/dependencies.ts @@ -6,8 +6,11 @@ * found in the LICENSE file at https://angular.dev/license */ -import { Tree } from '@angular-devkit/schematics'; +import { Rule, Tree } from '@angular-devkit/schematics'; +import { TestRunner } from '../ng-new/schema'; +import { DependencyType, ExistingBehavior, InstallBehavior, addDependency } from './dependency'; import { JSONFile } from './json-file'; +import { latestVersions } from './latest-versions'; const PKG_JSON_PATH = '/package.json'; export enum NodeDependencyType { @@ -78,3 +81,29 @@ export function getPackageJsonDependency( return null; } + +export function addTestRunnerDependencies( + testRunner: TestRunner | undefined, + skipInstall: boolean, +): Rule[] { + const dependencies = + testRunner === TestRunner.Vitest + ? ['vitest', 'jsdom'] + : [ + 'karma', + 'karma-chrome-launcher', + 'karma-coverage', + 'karma-jasmine', + 'karma-jasmine-html-reporter', + 'jasmine-core', + '@types/jasmine', + ]; + + return dependencies.map((name) => + addDependency(name, latestVersions[name], { + type: DependencyType.Dev, + existing: ExistingBehavior.Skip, + install: skipInstall ? InstallBehavior.None : InstallBehavior.Auto, + }), + ); +} diff --git a/packages/schematics/angular/utility/latest-versions/package.json b/packages/schematics/angular/utility/latest-versions/package.json index 0c6385202905..03db4d15a506 100644 --- a/packages/schematics/angular/utility/latest-versions/package.json +++ b/packages/schematics/angular/utility/latest-versions/package.json @@ -15,7 +15,7 @@ "karma-jasmine-html-reporter": "~2.1.0", "karma-jasmine": "~5.1.0", "karma": "~6.4.0", - "jsdom": "^27.0.0", + "jsdom": "^27.1.0", "less": "^4.2.0", "postcss": "^8.5.3", "protractor": "~7.0.0", @@ -25,7 +25,7 @@ "tslib": "^2.3.0", "ts-node": "~10.9.0", "typescript": "~5.9.2", - "vitest": "^4.0.0", - "zone.js": "~0.15.0" + "vitest": "^4.0.8", + "zone.js": "~0.16.0" } } diff --git a/packages/schematics/angular/workspace/files/README.md.template b/packages/schematics/angular/workspace/files/README.md.template index 85cba41ab1ea..3cf62f4dabcc 100644 --- a/packages/schematics/angular/workspace/files/README.md.template +++ b/packages/schematics/angular/workspace/files/README.md.template @@ -38,7 +38,7 @@ This will compile your project and store the build artifacts in the `dist/` dire ## Running unit tests -To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command: +To execute unit tests with the [Vitest](https://vitest.dev/) test runner, use the following command: ```bash ng test diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cd06474a4cd3..adec08d1979f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - typescript: 5.9.3 + undici-types: ^7.16.0 '@angular/build': workspace:* packageExtensionsChecksum: sha256-3L73Fw32UVtE6x5BJxJPBtQtH/mgsr31grNpdhHP1IY= @@ -20,47 +20,50 @@ importers: built: true devDependencies: '@angular/animations': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.0.1 + version: 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/cdk': - specifier: 21.0.0-next.10 - version: 21.0.0-next.10(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.0.0 + version: 21.0.0(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/common': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9 + specifier: 21.0.1 + version: 21.0.1 '@angular/compiler-cli': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3) + specifier: 21.0.1 + version: 21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3) '@angular/core': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + specifier: 21.0.1 + version: 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) '@angular/forms': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2) '@angular/localize': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3))(@angular/compiler@21.0.0-next.9) + specifier: 21.0.1 + version: 21.0.1(@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3))(@angular/compiler@21.0.1) '@angular/material': - specifier: 21.0.0-next.10 - version: 21.0.0-next.10(a08c742fd8cc4091bdee765e9534f381) + specifier: 21.0.0 + version: 21.0.0(8e7b236207d28338fccf527c5c162c22) '@angular/ng-dev': - specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#b69a61793bd6ba935af262297688408d0b48252e - version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/b69a61793bd6ba935af262297688408d0b48252e(@modelcontextprotocol/sdk@1.20.1) + specifier: https://github.com/angular/dev-infra-private-ng-dev-builds.git#4c28145df03aff8c74d0a53f4f5602140e4d1a23 + version: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23(@modelcontextprotocol/sdk@1.20.1) '@angular/platform-browser': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.0.1 + version: 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-server': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-next.9)(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.1)(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/router': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/service-worker': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@babel/core': + specifier: 7.28.4 + version: 7.28.4 '@bazel/bazelisk': specifier: 1.26.0 version: 1.26.0 @@ -81,7 +84,7 @@ importers: version: 5.1.1(rollup@4.52.5) '@rollup/plugin-commonjs': specifier: ^28.0.0 - version: 28.0.8(rollup@4.52.5) + version: 28.0.9(rollup@4.52.5) '@rollup/plugin-json': specifier: ^6.1.0 version: 6.1.0(rollup@4.52.5) @@ -90,7 +93,7 @@ importers: version: 16.0.3(rollup@4.52.5) '@stylistic/eslint-plugin': specifier: ^5.0.0 - version: 5.5.0(eslint@9.38.0(jiti@2.6.1)) + version: 5.6.1(eslint@9.38.0(jiti@2.6.1)) '@types/babel__core': specifier: 7.20.5 version: 7.20.5 @@ -99,19 +102,19 @@ importers: version: 7.27.0 '@types/browser-sync': specifier: ^2.27.0 - version: 2.29.0 + version: 2.29.1 '@types/express': specifier: ~5.0.1 - version: 5.0.3 + version: 5.0.5 '@types/http-proxy': specifier: ^1.17.4 - version: 1.17.16 + version: 1.17.17 '@types/ini': specifier: ^4.0.0 version: 4.1.1 '@types/jasmine': specifier: ~5.1.0 - version: 5.1.12 + version: 5.1.13 '@types/jasmine-reporters': specifier: ^2 version: 2.5.3 @@ -123,13 +126,13 @@ importers: version: 3.0.8 '@types/loader-utils': specifier: ^3.0.0 - version: 3.0.0(esbuild@0.25.11) + version: 3.0.0(esbuild@0.26.0) '@types/lodash': specifier: ^4.17.0 - version: 4.17.20 + version: 4.17.21 '@types/node': specifier: ^22.12.0 - version: 22.18.11 + version: 22.19.1 '@types/npm-package-arg': specifier: ^6.1.0 version: 6.1.4 @@ -150,10 +153,10 @@ importers: version: 7.7.1 '@types/watchpack': specifier: ^2.4.4 - version: 2.4.4 + version: 2.4.5 '@types/yargs': specifier: ^17.0.20 - version: 17.0.33 + version: 17.0.35 '@types/yargs-parser': specifier: ^21.0.0 version: 21.0.3 @@ -176,11 +179,11 @@ importers: specifier: 6.0.3 version: 6.0.3 esbuild: - specifier: 0.25.11 - version: 0.25.11 + specifier: 0.26.0 + version: 0.26.0 esbuild-wasm: - specifier: 0.25.11 - version: 0.25.11 + specifier: 0.26.0 + version: 0.26.0 eslint: specifier: 9.38.0 version: 9.38.0(jiti@2.6.1) @@ -216,7 +219,7 @@ importers: version: 5.12.0 jasmine-core: specifier: ~5.12.0 - version: 5.12.0 + version: 5.12.1 jasmine-reporters: specifier: ^2.5.2 version: 2.5.2 @@ -237,7 +240,7 @@ importers: version: 5.1.0(karma@6.4.4(bufferutil@4.0.9)) karma-jasmine-html-reporter: specifier: ~2.1.0 - version: 2.1.0(jasmine-core@5.12.0)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)))(karma@6.4.4(bufferutil@4.0.9)) + version: 2.1.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)))(karma@6.4.4(bufferutil@4.0.9)) karma-source-map-support: specifier: 1.4.0 version: 1.4.0 @@ -273,7 +276,7 @@ importers: version: 6.2.3(rollup@4.52.5)(typescript@5.9.3) rollup-plugin-sourcemaps2: specifier: 0.5.4 - version: 0.5.4(@types/node@22.18.11)(rollup@4.52.5) + version: 0.5.4(@types/node@22.19.1)(rollup@4.52.5) semver: specifier: 7.7.3 version: 7.7.3 @@ -282,10 +285,10 @@ importers: version: 0.5.21 tar: specifier: ^7.0.0 - version: 7.5.1 + version: 7.5.2 ts-node: specifier: ^10.9.1 - version: 10.9.2(@types/node@22.18.11)(typescript@5.9.3) + version: 10.9.2(@types/node@22.19.1)(typescript@5.9.3) tslib: specifier: 2.8.1 version: 2.8.1 @@ -329,17 +332,20 @@ importers: specifier: workspace:* version: link:../../../packages/angular/ssr '@vitest/coverage-v8': - specifier: 4.0.0 - version: 4.0.0(vitest@4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + specifier: 4.0.8 + version: 4.0.8(vitest@4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + browser-sync: + specifier: 3.0.4 + version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) jsdom: - specifier: 27.0.1 - version: 27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) + specifier: 27.1.0 + version: 27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) rxjs: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 4.0.0 - version: 4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + specifier: 4.0.8 + version: 4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) packages/angular/build: dependencies: @@ -360,19 +366,19 @@ importers: version: 7.24.7 '@inquirer/confirm': specifier: 5.1.19 - version: 5.1.19(@types/node@24.9.1) + version: 5.1.19(@types/node@24.10.1) '@vitejs/plugin-basic-ssl': specifier: 2.1.0 - version: 2.1.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + version: 2.1.0(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) beasties: specifier: 0.3.5 version: 0.3.5 browserslist: specifier: ^4.26.0 - version: 4.26.3 + version: 4.28.0 esbuild: - specifier: 0.25.11 - version: 0.25.11 + specifier: 0.26.0 + version: 0.26.0 https-proxy-agent: specifier: 7.0.6 version: 7.0.6(supports-color@10.2.2) @@ -401,8 +407,8 @@ importers: specifier: 5.1.3 version: 5.1.3 rolldown: - specifier: 1.0.0-beta.44 - version: 1.0.0-beta.44 + specifier: 1.0.0-beta.47 + version: 1.0.0-beta.47 sass: specifier: 1.93.2 version: 1.93.2 @@ -415,9 +421,12 @@ importers: tinyglobby: specifier: 0.2.15 version: 0.2.15 + undici: + specifier: 7.16.0 + version: 7.16.0 vite: - specifier: 7.1.11 - version: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + specifier: 7.2.2 + version: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) watchpack: specifier: 2.4.4 version: 2.4.4 @@ -429,14 +438,14 @@ importers: specifier: workspace:* version: link:../ssr jsdom: - specifier: 27.0.1 - version: 27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) + specifier: 27.1.0 + version: 27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) less: specifier: 4.4.2 version: 4.4.2 ng-packagr: - specifier: 21.0.0-next.4 - version: 21.0.0-next.4(@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.0.0 + version: 21.0.0(@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) postcss: specifier: 8.5.6 version: 8.5.6 @@ -444,8 +453,8 @@ importers: specifier: 7.8.2 version: 7.8.2 vitest: - specifier: 4.0.0 - version: 4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + specifier: 4.0.8 + version: 4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) optionalDependencies: lmdb: specifier: 3.4.3 @@ -464,10 +473,10 @@ importers: version: link:../../angular_devkit/schematics '@inquirer/prompts': specifier: 7.9.0 - version: 7.9.0(@types/node@24.9.1) + version: 7.9.0(@types/node@24.10.1) '@listr2/prompt-adapter-inquirer': specifier: 3.0.5 - version: 3.0.5(@inquirer/prompts@7.9.0(@types/node@24.9.1))(@types/node@24.9.1)(listr2@9.0.5) + version: 3.0.5(@inquirer/prompts@7.9.0(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5) '@modelcontextprotocol/sdk': specifier: 1.20.1 version: 1.20.1 @@ -533,23 +542,23 @@ importers: specifier: workspace:* version: link:../../angular_devkit/schematics '@angular/common': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) '@angular/compiler': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9 + specifier: 21.0.1 + version: 21.0.1 '@angular/core': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + specifier: 21.0.1 + version: 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) '@angular/platform-browser': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + specifier: 21.0.1 + version: 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) '@angular/platform-server': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-next.9)(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.1)(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@angular/router': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) + specifier: 21.0.1 + version: 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2) '@schematics/angular': specifier: workspace:* version: link:../../schematics/angular @@ -646,19 +655,19 @@ importers: version: 10.4.21(postcss@8.5.6) babel-loader: specifier: 10.0.0 - version: 10.0.0(@babel/core@7.28.4)(webpack@5.102.1(esbuild@0.25.11)) + version: 10.0.0(@babel/core@7.28.4)(webpack@5.102.1(esbuild@0.26.0)) browserslist: specifier: ^4.26.0 - version: 4.26.3 + version: 4.28.0 copy-webpack-plugin: specifier: 13.0.1 - version: 13.0.1(webpack@5.102.1(esbuild@0.25.11)) + version: 13.0.1(webpack@5.102.1(esbuild@0.26.0)) css-loader: specifier: 7.1.2 - version: 7.1.2(webpack@5.102.1(esbuild@0.25.11)) + version: 7.1.2(webpack@5.102.1(esbuild@0.26.0)) esbuild-wasm: - specifier: 0.25.11 - version: 0.25.11 + specifier: 0.26.0 + version: 0.26.0 http-proxy-middleware: specifier: 3.0.5 version: 3.0.5 @@ -676,16 +685,16 @@ importers: version: 4.4.2 less-loader: specifier: 12.3.0 - version: 12.3.0(less@4.4.2)(webpack@5.102.1(esbuild@0.25.11)) + version: 12.3.0(less@4.4.2)(webpack@5.102.1(esbuild@0.26.0)) license-webpack-plugin: specifier: 4.0.2 - version: 4.0.2(webpack@5.102.1(esbuild@0.25.11)) + version: 4.0.2(webpack@5.102.1(esbuild@0.26.0)) loader-utils: specifier: 3.3.1 version: 3.3.1 mini-css-extract-plugin: specifier: 2.9.4 - version: 2.9.4(webpack@5.102.1(esbuild@0.25.11)) + version: 2.9.4(webpack@5.102.1(esbuild@0.26.0)) open: specifier: 10.2.0 version: 10.2.0 @@ -703,7 +712,7 @@ importers: version: 8.5.6 postcss-loader: specifier: 8.2.0 - version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(esbuild@0.25.11)) + version: 8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(esbuild@0.26.0)) resolve-url-loader: specifier: 5.0.0 version: 5.0.0 @@ -715,13 +724,13 @@ importers: version: 1.93.2 sass-loader: specifier: 16.0.5 - version: 16.0.5(sass@1.93.2)(webpack@5.102.1(esbuild@0.25.11)) + version: 16.0.5(sass@1.93.2)(webpack@5.102.1(esbuild@0.26.0)) semver: specifier: 7.7.3 version: 7.7.3 source-map-loader: specifier: 5.0.0 - version: 5.0.0(webpack@5.102.1(esbuild@0.25.11)) + version: 5.0.0(webpack@5.102.1(esbuild@0.26.0)) source-map-support: specifier: 0.5.21 version: 0.5.21 @@ -739,19 +748,19 @@ importers: version: 2.8.1 webpack: specifier: 5.102.1 - version: 5.102.1(esbuild@0.25.11) + version: 5.102.1(esbuild@0.26.0) webpack-dev-middleware: specifier: 7.4.5 - version: 7.4.5(webpack@5.102.1(esbuild@0.25.11)) + version: 7.4.5(webpack@5.102.1(esbuild@0.26.0)) webpack-dev-server: specifier: 5.2.2 - version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.25.11)) + version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.26.0)) webpack-merge: specifier: 6.0.1 version: 6.0.1 webpack-subresource-integrity: specifier: 5.1.0 - version: 5.1.0(webpack@5.102.1(esbuild@0.25.11)) + version: 5.1.0(webpack@5.102.1(esbuild@0.26.0)) devDependencies: '@angular/ssr': specifier: workspace:* @@ -761,17 +770,17 @@ importers: version: 0.20.2(bufferutil@4.0.9) browser-sync: specifier: 3.0.4 - version: 3.0.4(bufferutil@4.0.9) + version: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) ng-packagr: - specifier: 21.0.0-next.4 - version: 21.0.0-next.4(@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) + specifier: 21.0.0 + version: 21.0.0(@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3) undici: specifier: 7.16.0 version: 7.16.0 optionalDependencies: esbuild: - specifier: 0.25.11 - version: 0.25.11 + specifier: 0.26.0 + version: 0.26.0 packages/angular_devkit/build_webpack: dependencies: @@ -790,10 +799,10 @@ importers: version: link:../../ngtools/webpack webpack: specifier: 5.102.1 - version: 5.102.1(esbuild@0.25.11) + version: 5.102.1(esbuild@0.26.0) webpack-dev-server: specifier: 5.2.2 - version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.25.11)) + version: 5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.26.0)) packages/angular_devkit/core: dependencies: @@ -848,7 +857,7 @@ importers: version: link:../schematics '@inquirer/prompts': specifier: 7.9.0 - version: 7.9.0(@types/node@24.9.1) + version: 7.9.0(@types/node@24.10.1) ansi-colors: specifier: 4.1.3 version: 4.1.3 @@ -862,17 +871,17 @@ importers: specifier: workspace:0.0.0-PLACEHOLDER version: link:../../angular_devkit/core '@angular/compiler': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9 + specifier: 21.0.1 + version: 21.0.1 '@angular/compiler-cli': - specifier: 21.0.0-next.9 - version: 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3) + specifier: 21.0.1 + version: 21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3) typescript: specifier: 5.9.3 version: 5.9.3 webpack: specifier: 5.102.1 - version: 5.102.1(esbuild@0.25.11) + version: 5.102.1(esbuild@0.26.0) packages/schematics/angular: dependencies: @@ -900,6 +909,9 @@ importers: packages: + '@acemir/cssom@0.9.24': + resolution: {integrity: sha512-5YjgMmAiT2rjJZU7XK1SNI7iqTy92DpaYVgG6x63FxkJ11UpYfLndHJATtinWJClAXiOlW9XWaUyAQf8pMrQPg==} + '@actions/core@1.11.1': resolution: {integrity: sha512-hXJCSrkwfA46Vd9Z3q4cpEpHB1rL5NG04+/rbqW9d3+CSvtB1tYe8UTpAlixa1vj0m/ULglfEK2UKxMGxCxv5A==} @@ -972,130 +984,130 @@ packages: resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} - '@angular/animations@21.0.0-next.9': - resolution: {integrity: sha512-tvZ3vajgMXBUqej90sKg4NG6EqvpSYlsZcdfJLn8GtIZRvVyo5FhFXPj8O6ybKfIemmcxpACjKikpMRRx50C4A==} + '@angular/animations@21.0.1': + resolution: {integrity: sha512-P7i/jpNnzXwo0vHEG0cDXYojwTz0WQlXJHrmOJzLVveyfcFwgXYXJxhGGUI2+k21YrlJTKkR/4QZTEJ0GP0f8Q==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.0.0-next.9 + '@angular/core': 21.0.1 - '@angular/cdk@21.0.0-next.10': - resolution: {integrity: sha512-2TrKv6q92eWHx+pB0WcignKqBnW6dY+1lFA1Nh0Dg6Al+/ZWhPP89dQUZylWlRQqKlolClJulRfE5PQXidlOIA==} + '@angular/cdk@21.0.0': + resolution: {integrity: sha512-wCr5D3mEC+p69IMDC7vf8bWx18mfUNNRdsiK3XD0m1PqfeNfnCJb+Bnkks37MC/SU01uCNrAokRaTbWL6pk1Wg==} peerDependencies: - '@angular/common': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 - '@angular/core': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 + '@angular/common': ^21.0.0 || ^22.0.0 + '@angular/core': ^21.0.0 || ^22.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/common@21.0.0-next.9': - resolution: {integrity: sha512-0mXLVCbJxc9WJZJDU1Qf6o5lLORNC4SZuZp1u9cqE/JKCFK4Pt1uu7WFdnPxgu+jN9JiHc4EQJqiINz7nraQ8A==} + '@angular/common@21.0.1': + resolution: {integrity: sha512-EqdTGpFp7PVdTVztO7TB6+QxdzUbYXKKT2jwG2Gg+PIQZ2A8XrLPRmGXyH/DLlc5IhnoJlLbngmBRCLCO4xWog==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/core': 21.0.0-next.9 + '@angular/core': 21.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@angular/compiler-cli@21.0.0-next.9': - resolution: {integrity: sha512-OFczNXCZK3PhlX8QI9YuoAYfz0/qKgmCzy2g/Za+Qf5HyTFHxyB0itAdC+vzUmzyP3vvxjLhXsYfdvC6jEe0Cg==} + '@angular/compiler-cli@21.0.1': + resolution: {integrity: sha512-BxGLtL5bxlaaAs/kSN4oyXhMfvzqsj1Gc4Jauz39R4xtgOF5cIvjBtj6dJ9mD3PK0s6zaFi7WYd0YwWkxhjgMA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.0.0-next.9 - typescript: 5.9.3 + '@angular/compiler': 21.0.1 + typescript: '>=5.9 <6.0' peerDependenciesMeta: typescript: optional: true - '@angular/compiler@21.0.0-next.9': - resolution: {integrity: sha512-C+qkZvukOIhtXzb6ownhaWaryJGfVu0/JO6xswCY59wgT5EN7kR7rB+VjSIbtEvn0us0i248/5Y3Wf6xX5VF1A==} + '@angular/compiler@21.0.1': + resolution: {integrity: sha512-YRzHpThgCaC9b3xzK1Wx859ePeHEPR7ewQklUB5TYbpzVacvnJo38PcSAx/nzOmgX9y4mgyros6LzECmBb8d8w==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} - '@angular/core@21.0.0-next.9': - resolution: {integrity: sha512-2fcMVzV7o0vavbF/6YyjMyj27pnYdBA+/r/buaDnrBHRYgq0kKO6tdwnCJR9hX36Tm0pHS7+LY/VVqVJEJGKFQ==} + '@angular/core@21.0.1': + resolution: {integrity: sha512-z0G9Bwzgqr0fQVbtMgqwl+SbbiqtJD7I2xT6U5p45LetKHojcfigH29dxi/vqALPwEdgb2nSIx7RqVhoyynraQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/compiler': 21.0.0-next.9 + '@angular/compiler': 21.0.1 rxjs: ^6.5.3 || ^7.4.0 - zone.js: ~0.15.0 + zone.js: ~0.15.0 || ~0.16.0 peerDependenciesMeta: '@angular/compiler': optional: true zone.js: optional: true - '@angular/forms@21.0.0-next.9': - resolution: {integrity: sha512-YAJj/KDMPzsTb2qHgak/OfaSr04WvAo6ddcTrUUqPd4KNIz2mhFncm6olytjgdwBNUB9DGKya/UGqcaZ0XQQPg==} + '@angular/forms@21.0.1': + resolution: {integrity: sha512-BVFPuKjxkzjzKMmpc6KxUKICpVs6J2/KzA4HjtPp/UKvdZPe8dj8vIXuc9pGf8DA4XdkjCwvv8szCgzTWi02LQ==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-next.9 - '@angular/core': 21.0.0-next.9 - '@angular/platform-browser': 21.0.0-next.9 + '@angular/common': 21.0.1 + '@angular/core': 21.0.1 + '@angular/platform-browser': 21.0.1 '@standard-schema/spec': ^1.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/localize@21.0.0-next.9': - resolution: {integrity: sha512-WXQ/JI6/k6OMwf8FJ2Hj2kbsa6aIoDMB3rxiDzsQBP9vYOwN5B/i8dHWU26JPcKHVHg/mjm2+DBbzWU1d4TvHw==} + '@angular/localize@21.0.1': + resolution: {integrity: sha512-ouzParJQEQO9cStBkIioyDkQ839eJaedSwNuNXK/MeTZ1NH+pDcvwgYH0Gcn5BTB7fZEY0oCXyBcVxyz37qTcA==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler': 21.0.0-next.9 - '@angular/compiler-cli': 21.0.0-next.9 + '@angular/compiler': 21.0.1 + '@angular/compiler-cli': 21.0.1 - '@angular/material@21.0.0-next.10': - resolution: {integrity: sha512-oh3SA4b4SLqBpPEWYO00LOSQKUSrSM96ladmr2O7bOpHk4VfRJBaSiX3Tkr/K1bsNrg88LZp3lBYtMN7soIxoQ==} + '@angular/material@21.0.0': + resolution: {integrity: sha512-s3+fhN7F5T1TAltZXYXOgY1wuVbICCrBJpV2TN8nJXDT0wroTYAljgBmsr6ZjDwYJewwP0OPvcj2NlOGDpa6oA==} peerDependencies: - '@angular/cdk': 21.0.0-next.10 - '@angular/common': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 - '@angular/core': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 - '@angular/forms': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 - '@angular/platform-browser': ^21.0.0-0 || ^21.1.0-0 || ^21.2.0-0 || ^21.3.0-0 || ^22.0.0-0 + '@angular/cdk': 21.0.0 + '@angular/common': ^21.0.0 || ^22.0.0 + '@angular/core': ^21.0.0 || ^22.0.0 + '@angular/forms': ^21.0.0 || ^22.0.0 + '@angular/platform-browser': ^21.0.0 || ^22.0.0 rxjs: ^6.5.3 || ^7.4.0 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/b69a61793bd6ba935af262297688408d0b48252e': - resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/b69a61793bd6ba935af262297688408d0b48252e} - version: 0.0.0-ab6a00e9a219c2169ae0540cc5a32be5f481e004 + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23': + resolution: {tarball: https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23} + version: 0.0.0-95e3a0ede6dfa1aedd34b03918ad72b18f87e5ae hasBin: true - '@angular/platform-browser@21.0.0-next.9': - resolution: {integrity: sha512-kWVbVRQqyX75grB9EfS9P4UbXSNg7vga7zPd5r67J9hj3Wg0y4Y+iIFdLavPp3uFHqPj/nI/hIgYCzKNjVp+Zg==} + '@angular/platform-browser@21.0.1': + resolution: {integrity: sha512-68StH9HILKUqNhQKz6KKNHzpgk1n88CIusWlmJvnb0l6iWGf3ydq5lTMKAKiZQmSDAVP5unTGfNvIkh59GRyVg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/animations': 21.0.0-next.9 - '@angular/common': 21.0.0-next.9 - '@angular/core': 21.0.0-next.9 + '@angular/animations': 21.0.1 + '@angular/common': 21.0.1 + '@angular/core': 21.0.1 peerDependenciesMeta: '@angular/animations': optional: true - '@angular/platform-server@21.0.0-next.9': - resolution: {integrity: sha512-Nwks5bIIZMI1bn5ya8Z3QBE8MhRYsLgw+Z9njY7qZLHxQLpFxcgGsnkBa9V2WSgm8CMiwKunGOzht25zXDj6Ww==} + '@angular/platform-server@21.0.1': + resolution: {integrity: sha512-SfDn9u2YG2UaFtuUL35UFedTz6xiMphVD2sbLLYPpYVd3nRZUziUV3VjeZ2xM6mbZTtT4m2zA8XGUbbWVkxOPg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-next.9 - '@angular/compiler': 21.0.0-next.9 - '@angular/core': 21.0.0-next.9 - '@angular/platform-browser': 21.0.0-next.9 + '@angular/common': 21.0.1 + '@angular/compiler': 21.0.1 + '@angular/core': 21.0.1 + '@angular/platform-browser': 21.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@angular/router@21.0.0-next.9': - resolution: {integrity: sha512-1WnTT4f1b8Otfq66az1a/3JXOcZmL6/akKLDJENQ0TxYnKfphQ5k7n69q/ZuSrXCSN46jnLRxfyuN5/seJPz0g==} + '@angular/router@21.0.1': + resolution: {integrity: sha512-EnNbiScESZ0op9XS9qUNncWc1UcSYy90uCbDMVTTChikZt9b+e19OusFMf50zecb96VMMz+BzNY1see7Rmvx4g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: - '@angular/common': 21.0.0-next.9 - '@angular/core': 21.0.0-next.9 - '@angular/platform-browser': 21.0.0-next.9 + '@angular/common': 21.0.1 + '@angular/core': 21.0.1 + '@angular/platform-browser': 21.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@angular/service-worker@21.0.0-next.9': - resolution: {integrity: sha512-7hvj4f4NyxD42hEMj8qBILvto2jL61EJ4aVegEHtQ1GP2rpuIJta6JqRAaPyWiFgTJJxc/FEI7u/aLLRIsA6PQ==} + '@angular/service-worker@21.0.1': + resolution: {integrity: sha512-O2ebI7dSiSHjn8QoLQtwLcRsWEqzEwF6o36BSn4tb82q6oufG9txTyZBbqQgGIyBdDhvwVk4z7IdAUHwtrlQjg==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/core': 21.0.0-next.9 + '@angular/core': 21.0.1 rxjs: ^6.5.3 || ^7.4.0 - '@asamuzakjp/css-color@4.0.5': - resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==} + '@asamuzakjp/css-color@4.1.0': + resolution: {integrity: sha512-9xiBAtLn4aNsa4mDnpovJvBn72tNEIACyvlqaNJ+ADemR+yeMJWnBudOi2qGDviJa7SwcDOU/TRh5dnET7qk0w==} - '@asamuzakjp/dom-selector@6.7.2': - resolution: {integrity: sha512-ccKogJI+0aiDhOahdjANIc9SDixSud1gbwdVrhn7kMopAtLXqsz9MKmQQtIl6Y5aC2IYq+j4dz/oedL2AVMmVQ==} + '@asamuzakjp/dom-selector@6.7.4': + resolution: {integrity: sha512-buQDjkm+wDPXd6c13534URWZqbz0RP5PAhXZ+LIoa5LgwInT9HVJvGIJivg75vi8I13CxDGdTnz+aY5YUJlIAA==} '@asamuzakjp/nwsapi@2.3.9': resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==} @@ -1104,8 +1116,8 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.4': - resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + '@babel/compat-data@7.28.5': + resolution: {integrity: sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==} engines: {node: '>=6.9.0'} '@babel/core@7.28.4': @@ -1116,6 +1128,10 @@ packages: resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} engines: {node: '>=6.9.0'} + '@babel/generator@7.28.5': + resolution: {integrity: sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==} + engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -1124,14 +1140,14 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.3': - resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + '@babel/helper-create-class-features-plugin@7.28.5': + resolution: {integrity: sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-create-regexp-features-plugin@7.27.1': - resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==} + '@babel/helper-create-regexp-features-plugin@7.28.5': + resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1145,8 +1161,8 @@ packages: resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.27.1': - resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} + '@babel/helper-member-expression-to-functions@7.28.5': + resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.27.1': @@ -1191,8 +1207,8 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.27.1': - resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.27.1': @@ -1207,13 +1223,13 @@ packages: resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.4': - resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + '@babel/parser@7.28.5': + resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==} engines: {node: '>=6.0.0'} hasBin: true - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1': - resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==} + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': + resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -1290,8 +1306,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.4': - resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==} + '@babel/plugin-transform-block-scoping@7.28.5': + resolution: {integrity: sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1320,8 +1336,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.0': - resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + '@babel/plugin-transform-destructuring@7.28.5': + resolution: {integrity: sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1356,8 +1372,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.27.1': - resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} + '@babel/plugin-transform-exponentiation-operator@7.28.5': + resolution: {integrity: sha512-D4WIMaFtwa2NizOp+dnoFjRez/ClKiC2BqqImwKd1X28nqBtZEyCYJ2ozQrrzlxAFrcrjxo39S6khe9RNDlGzw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1392,8 +1408,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-logical-assignment-operators@7.27.1': - resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==} + '@babel/plugin-transform-logical-assignment-operators@7.28.5': + resolution: {integrity: sha512-axUuqnUTBuXyHGcJEVVh9pORaN6wC5bYfE7FGzPiaWa3syib9m7g+/IT/4VgCOe2Upef43PHzeAvcrVek6QuuA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1416,8 +1432,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-modules-systemjs@7.27.1': - resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==} + '@babel/plugin-transform-modules-systemjs@7.28.5': + resolution: {integrity: sha512-vn5Jma98LCOeBy/KpeQhXcV2WZgaRUtjwQmjoBuLNlOmkg0fB5pdvYVeWRYI69wWKwK2cD1QbMiUQnoujWvrew==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1470,8 +1486,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-optional-chaining@7.27.1': - resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==} + '@babel/plugin-transform-optional-chaining@7.28.5': + resolution: {integrity: sha512-N6fut9IZlPnjPwgiQkXNhb+cT8wQKFlJNqcZkWlcTqkcqx6/kU4ynGmLFoa4LViBSirn05YAwk+sQBbPfxtYzQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1597,12 +1613,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.4': - resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + '@babel/traverse@7.28.5': + resolution: {integrity: sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.4': - resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + '@babel/types@7.28.5': + resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==} engines: {node: '>=6.9.0'} '@bazel/bazelisk@1.26.0': @@ -1661,11 +1677,9 @@ packages: peerDependencies: '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.14': - resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==} + '@csstools/css-syntax-patches-for-csstree@1.0.17': + resolution: {integrity: sha512-LCC++2h8pLUSPY+EsZmrrJ1EOUu+5iClpEiDhhdw3zRJpPbABML/N5lmRuBHjxtKm9VnRcsUzioyD0sekFMF0A==} engines: {node: '>=18'} - peerDependencies: - postcss: ^8.4 '@csstools/css-tokenizer@3.0.4': resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} @@ -1679,167 +1693,479 @@ packages: resolution: {integrity: sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==} engines: {node: '>=14.17.0'} - '@emnapi/core@1.5.0': - resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + '@emnapi/core@1.7.1': + resolution: {integrity: sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.7.1': + resolution: {integrity: sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==} '@emnapi/wasi-threads@1.1.0': resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} - '@esbuild/aix-ppc64@0.25.11': - resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==} + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.26.0': + resolution: {integrity: sha512-hj0sKNCQOOo2fgyII3clmJXP28VhgDfU5iy3GNHlWO76KG6N7x4D9ezH5lJtQTG+1J6MFDAJXC1qsI+W+LvZoA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/aix-ppc64@0.27.0': + resolution: {integrity: sha512-KuZrd2hRjz01y5JK9mEBSD3Vj3mbCvemhT466rSuJYeE/hjuBrHfjjcjMdTm/sz7au+++sdbJZJmuBwQLuw68A==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.11': - resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==} + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.26.0': + resolution: {integrity: sha512-DDnoJ5eoa13L8zPh87PUlRd/IyFaIKOlRbxiwcSbeumcJ7UZKdtuMCHa1Q27LWQggug6W4m28i4/O2qiQQ5NZQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm64@0.27.0': + resolution: {integrity: sha512-CC3vt4+1xZrs97/PKDkl0yN7w8edvU2vZvAFGD16n9F0Cvniy5qvzRXjfO1l94efczkkQE6g1x0i73Qf5uthOQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.11': - resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==} + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.11': - resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==} + '@esbuild/android-arm@0.26.0': + resolution: {integrity: sha512-C0hkDsYNHZkBtPxxDx177JN90/1MiCpvBNjz1f5yWJo1+5+c5zr8apjastpEG+wtPjo9FFtGG7owSsAxyKiHxA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-arm@0.27.0': + resolution: {integrity: sha512-j67aezrPNYWJEOHUNLPj9maeJte7uSMM6gMoxfPC9hOg8N02JuQi/T7ewumf4tNvJadFkvLZMlAq73b9uwdMyQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.11': - resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==} + '@esbuild/android-x64@0.26.0': + resolution: {integrity: sha512-bKDkGXGZnj0T70cRpgmv549x38Vr2O3UWLbjT2qmIkdIWcmlg8yebcFWoT9Dku7b5OV3UqPEuNKRzlNhjwUJ9A==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/android-x64@0.27.0': + resolution: {integrity: sha512-wurMkF1nmQajBO1+0CJmcN17U4BP6GqNSROP8t0X/Jiw2ltYGLHpEksp9MpoBqkrFR3kv2/te6Sha26k3+yZ9Q==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-arm64@0.26.0': + resolution: {integrity: sha512-6Z3naJgOuAIB0RLlJkYc81An3rTlQ/IeRdrU3dOea8h/PvZSgitZV+thNuIccw0MuK1GmIAnAmd5TrMZad8FTQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.11': - resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==} + '@esbuild/darwin-arm64@0.27.0': + resolution: {integrity: sha512-uJOQKYCcHhg07DL7i8MzjvS2LaP7W7Pn/7uA0B5S1EnqAirJtbyw4yC5jQ5qcFjHK9l6o/MX9QisBg12kNkdHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.11': - resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==} + '@esbuild/darwin-x64@0.26.0': + resolution: {integrity: sha512-OPnYj0zpYW0tHusMefyaMvNYQX5pNQuSsHFTHUBNp3vVXupwqpxofcjVsUx11CQhGVkGeXjC3WLjh91hgBG2xw==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.0': + resolution: {integrity: sha512-8mG6arH3yB/4ZXiEnXof5MK72dE6zM9cDvUcPtxhUZsDjESl9JipZYW60C3JGreKCEP+p8P/72r69m4AZGJd5g==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.11': - resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==} + '@esbuild/freebsd-arm64@0.26.0': + resolution: {integrity: sha512-jix2fa6GQeZhO1sCKNaNMjfj5hbOvoL2F5t+w6gEPxALumkpOV/wq7oUBMHBn2hY2dOm+mEV/K+xfZy3mrsxNQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-arm64@0.27.0': + resolution: {integrity: sha512-9FHtyO988CwNMMOE3YIeci+UV+x5Zy8fI2qHNpsEtSF83YPBmE8UWmfYAQg6Ux7Gsmd4FejZqnEUZCMGaNQHQw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.26.0': + resolution: {integrity: sha512-tccJaH5xHJD/239LjbVvJwf6T4kSzbk6wPFerF0uwWlkw/u7HL+wnAzAH5GB2irGhYemDgiNTp8wJzhAHQ64oA==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.0': + resolution: {integrity: sha512-zCMeMXI4HS/tXvJz8vWGexpZj2YVtRAihHLk1imZj4efx1BQzN76YFeKqlDr3bUWI26wHwLWPd3rwh6pe4EV7g==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.11': - resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==} + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.11': - resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==} + '@esbuild/linux-arm64@0.26.0': + resolution: {integrity: sha512-IMJYN7FSkLttYyTbsbme0Ra14cBO5z47kpamo16IwggzzATFY2lcZAwkbcNkWiAduKrTgFJP7fW5cBI7FzcuNQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm64@0.27.0': + resolution: {integrity: sha512-AS18v0V+vZiLJyi/4LphvBE+OIX682Pu7ZYNsdUHyUKSoRwdnOsMf6FDekwoAFKej14WAkOef3zAORJgAtXnlQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.11': - resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==} + '@esbuild/linux-arm@0.26.0': + resolution: {integrity: sha512-JY8NyU31SyRmRpuc5W8PQarAx4TvuYbyxbPIpHAZdr/0g4iBr8KwQBS4kiiamGl2f42BBecHusYCsyxi7Kn8UQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-arm@0.27.0': + resolution: {integrity: sha512-t76XLQDpxgmq2cNXKTVEB7O7YMb42atj2Re2Haf45HkaUpjM2J0UuJZDuaGbPbamzZ7bawyGFUkodL+zcE+jvQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-ia32@0.26.0': + resolution: {integrity: sha512-XITaGqGVLgk8WOHw8We9Z1L0lbLFip8LyQzKYFKO4zFo1PFaaSKsbNjvkb7O8kEXytmSGRkYpE8LLVpPJpsSlw==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.11': - resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==} + '@esbuild/linux-ia32@0.27.0': + resolution: {integrity: sha512-Mz1jxqm/kfgKkc/KLHC5qIujMvnnarD9ra1cEcrs7qshTUSksPihGrWHVG5+osAIQ68577Zpww7SGapmzSt4Nw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-loong64@0.26.0': + resolution: {integrity: sha512-MkggfbDIczStUJwq9wU7gQ7kO33d8j9lWuOCDifN9t47+PeI+9m2QVh51EI/zZQ1spZtFMC1nzBJ+qNGCjJnsg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.11': - resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==} + '@esbuild/linux-loong64@0.27.0': + resolution: {integrity: sha512-QbEREjdJeIreIAbdG2hLU1yXm1uu+LTdzoq1KCo4G4pFOLlvIspBm36QrQOar9LFduavoWX2msNFAAAY9j4BDg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.11': - resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==} + '@esbuild/linux-mips64el@0.26.0': + resolution: {integrity: sha512-fUYup12HZWAeccNLhQ5HwNBPr4zXCPgUWzEq2Rfw7UwqwfQrFZ0SR/JljaURR8xIh9t+o1lNUFTECUTmaP7yKA==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-mips64el@0.27.0': + resolution: {integrity: sha512-sJz3zRNe4tO2wxvDpH/HYJilb6+2YJxo/ZNbVdtFiKDufzWq4JmKAiHy9iGoLjAV7r/W32VgaHGkk35cUXlNOg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.26.0': + resolution: {integrity: sha512-MzRKhM0Ip+//VYwC8tialCiwUQ4G65WfALtJEFyU0GKJzfTYoPBw5XNWf0SLbCUYQbxTKamlVwPmcw4DgZzFxg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-ppc64@0.27.0': + resolution: {integrity: sha512-z9N10FBD0DCS2dmSABDBb5TLAyF1/ydVb+N4pi88T45efQ/w4ohr/F/QYCkxDPnkhkp6AIpIcQKQ8F0ANoA2JA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.11': - resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==} + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.26.0': + resolution: {integrity: sha512-QhCc32CwI1I4Jrg1enCv292sm3YJprW8WHHlyxJhae/dVs+KRWkbvz2Nynl5HmZDW/m9ZxrXayHzjzVNvQMGQA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.0': + resolution: {integrity: sha512-pQdyAIZ0BWIC5GyvVFn5awDiO14TkT/19FTmFcPdDec94KJ1uZcmFs21Fo8auMXzD4Tt+diXu1LW1gHus9fhFQ==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.11': - resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==} + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.11': - resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==} + '@esbuild/linux-s390x@0.26.0': + resolution: {integrity: sha512-1D6vi6lfI18aNT1aTf2HV+RIlm6fxtlAp8eOJ4mmnbYmZ4boz8zYDar86sIYNh0wmiLJEbW/EocaKAX6Yso2fw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-s390x@0.27.0': + resolution: {integrity: sha512-hPlRWR4eIDDEci953RI1BLZitgi5uqcsjKMxwYfmi4LcwyWo2IcRP+lThVnKjNtk90pLS8nKdroXYOqW+QQH+w==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/linux-x64@0.26.0': + resolution: {integrity: sha512-rnDcepj7LjrKFvZkx+WrBv6wECeYACcFjdNPvVPojCPJD8nHpb3pv3AuR9CXgdnjH1O23btICj0rsp0L9wAnHA==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.11': - resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==} + '@esbuild/linux-x64@0.27.0': + resolution: {integrity: sha512-1hBWx4OUJE2cab++aVZ7pObD6s+DK4mPGpemtnAORBvb5l/g5xFGk0vc0PjSkrDs0XaXj9yyob3d14XqvnQ4gw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.11': - resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==} + '@esbuild/netbsd-arm64@0.26.0': + resolution: {integrity: sha512-FSWmgGp0mDNjEXXFcsf12BmVrb+sZBBBlyh3LwB/B9ac3Kkc8x5D2WimYW9N7SUkolui8JzVnVlWh7ZmjCpnxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-arm64@0.27.0': + resolution: {integrity: sha512-6m0sfQfxfQfy1qRuecMkJlf1cIzTOgyaeXaiVaaki8/v+WB+U4hc6ik15ZW6TAllRlg/WuQXxWj1jx6C+dfy3w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.26.0': + resolution: {integrity: sha512-0QfciUDFryD39QoSPUDshj4uNEjQhp73+3pbSAaxjV2qGOEDsM67P7KbJq7LzHoVl46oqhIhJ1S+skKGR7lMXA==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.11': - resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==} + '@esbuild/netbsd-x64@0.27.0': + resolution: {integrity: sha512-xbbOdfn06FtcJ9d0ShxxvSn2iUsGd/lgPIO2V3VZIPDbEaIj1/3nBBe1AwuEZKXVXkMmpr6LUAgMkLD/4D2PPA==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.26.0': + resolution: {integrity: sha512-vmAK+nHhIZWImwJ3RNw9hX3fU4UGN/OqbSE0imqljNbUQC3GvVJ1jpwYoTfD6mmXmQaxdJY6Hn4jQbLGJKg5Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-arm64@0.27.0': + resolution: {integrity: sha512-fWgqR8uNbCQ/GGv0yhzttj6sU/9Z5/Sv/VGU3F5OuXK6J6SlriONKrQ7tNlwBrJZXRYk5jUhuWvF7GYzGguBZQ==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.11': - resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==} + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.11': - resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==} + '@esbuild/openbsd-x64@0.26.0': + resolution: {integrity: sha512-GPXF7RMkJ7o9bTyUsnyNtrFMqgM3X+uM/LWw4CeHIjqc32fm0Ir6jKDnWHpj8xHFstgWDUYseSABK9KCkHGnpg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.0': + resolution: {integrity: sha512-aCwlRdSNMNxkGGqQajMUza6uXzR/U0dIl1QmLjPtRbLOx3Gy3otfFu/VjATy4yQzo9yFDGTxYDo1FfAD9oRD2A==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.26.0': + resolution: {integrity: sha512-nUHZ5jEYqbBthbiBksbmHTlbb5eElyVfs/s1iHQ8rLBq1eWsd5maOnDpCocw1OM8kFK747d1Xms8dXJHtduxSw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/openharmony-arm64@0.27.0': + resolution: {integrity: sha512-nyvsBccxNAsNYz2jVFYwEGuRRomqZ149A39SHWk4hV0jWxKM0hjBPm3AmdxcbHiFLbBSwG6SbpIcUbXjgyECfA==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] - '@esbuild/sunos-x64@0.25.11': - resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==} + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.11': - resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==} + '@esbuild/sunos-x64@0.26.0': + resolution: {integrity: sha512-TMg3KCTCYYaVO+R6P5mSORhcNDDlemUVnUbb8QkboUtOhb5JWKAzd5uMIMECJQOxHZ/R+N8HHtDF5ylzLfMiLw==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/sunos-x64@0.27.0': + resolution: {integrity: sha512-Q1KY1iJafM+UX6CFEL+F4HRTgygmEW568YMqDA5UV97AuZSm21b7SXIrRJDwXWPzr8MGr75fUZPV67FdtMHlHA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-arm64@0.26.0': + resolution: {integrity: sha512-apqYgoAUd6ZCb9Phcs8zN32q6l0ZQzQBdVXOofa6WvHDlSOhwCWgSfVQabGViThS40Y1NA4SCvQickgZMFZRlA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.11': - resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==} + '@esbuild/win32-arm64@0.27.0': + resolution: {integrity: sha512-W1eyGNi6d+8kOmZIwi/EDjrL9nxQIQ0MiGqe/AWc6+IaHloxHSGoeRgDRKHFISThLmsewZ5nHFvGFWdBYlgKPg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.26.0': + resolution: {integrity: sha512-FGJAcImbJNZzLWu7U6WB0iKHl4RuY4TsXEwxJPl9UZLS47agIZuILZEX3Pagfw7I4J3ddflomt9f0apfaJSbaw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-ia32@0.27.0': + resolution: {integrity: sha512-30z1aKL9h22kQhilnYkORFYt+3wp7yZsHWus+wSKAJR8JtdfI76LJ4SBdMsCopTR3z/ORqVu5L1vtnHZWVj4cQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.11': - resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==} + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.26.0': + resolution: {integrity: sha512-WAckBKaVnmFqbEhbymrPK7M086DQMpL1XoRbpmN0iW8k5JSXjDRQBhcZNa0VweItknLq9eAeCL34jK7/CDcw7A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@esbuild/win32-x64@0.27.0': + resolution: {integrity: sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -1850,8 +2176,8 @@ packages: peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/regexpp@4.12.1': - resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/compat@1.4.0': @@ -1867,14 +2193,18 @@ packages: resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.4.1': - resolution: {integrity: sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==} + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/core@0.16.0': resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1887,16 +2217,16 @@ packages: resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.4.0': - resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@fastify/busboy@2.1.1': resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@firebase/ai@2.4.0': - resolution: {integrity: sha512-YilG6AJ/nYpCKtxZyvEzBRAQv5bU+2tBOKX4Ps0rNNSdxN39aT37kGhjATbk1kq1z5Lq7mkWglw/ajAF3lOWUg==} + '@firebase/ai@2.6.0': + resolution: {integrity: sha512-NGyE7NQDFznOv683Xk4+WoUv39iipa9lEfrwvvPz33ChzVbCCiB69FJQTK2BI/11pRtzYGbHo1/xMz7gxWWhJw==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -1933,19 +2263,19 @@ packages: peerDependencies: '@firebase/app': 0.x - '@firebase/app-compat@0.5.4': - resolution: {integrity: sha512-T7ifGmb+awJEcp542Ek4HtNfBxcBrnuk1ggUdqyFEdsXHdq7+wVlhvE6YukTL7NS8hIkEfL7TMAPx/uCNqt30g==} + '@firebase/app-compat@0.5.6': + resolution: {integrity: sha512-YYGARbutghQY4zZUWMYia0ib0Y/rb52y72/N0z3vglRHL7ii/AaK9SA7S/dzScVOlCdnbHXz+sc5Dq+r8fwFAg==} engines: {node: '>=20.0.0'} '@firebase/app-types@0.9.3': resolution: {integrity: sha512-kRVpIl4vVGJ4baogMDINbyrIOtOxqhkZQg4jTq3l8Lw6WSk0xfpEYzezFu+Kl4ve4fbPl79dvwRtaFqAC/ucCw==} - '@firebase/app@0.14.4': - resolution: {integrity: sha512-pUxEGmR+uu21OG/icAovjlu1fcYJzyVhhT0rsCrn+zi+nHtrS43Bp9KPn9KGa4NMspCUE++nkyiqziuIvJdwzw==} + '@firebase/app@0.14.6': + resolution: {integrity: sha512-4uyt8BOrBsSq6i4yiOV/gG6BnnrvTeyymlNcaN/dKvyU1GoolxAafvIvaNP1RCGPlNab3OuE4MKUQuv2lH+PLQ==} engines: {node: '>=20.0.0'} - '@firebase/auth-compat@0.6.0': - resolution: {integrity: sha512-J0lGSxXlG/lYVi45wbpPhcWiWUMXevY4fvLZsN1GHh+po7TZVng+figdHBVhFheaiipU8HZyc7ljw1jNojM2nw==} + '@firebase/auth-compat@0.6.1': + resolution: {integrity: sha512-I0o2ZiZMnMTOQfqT22ur+zcGDVSAfdNZBHo26/Tfi8EllfR1BO7aTVo2rt/ts8o/FWsK8pOALLeVBGhZt8w/vg==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app-compat': 0.x @@ -1959,8 +2289,8 @@ packages: '@firebase/app-types': 0.x '@firebase/util': 1.x - '@firebase/auth@1.11.0': - resolution: {integrity: sha512-5j7+ua93X+IRcJ1oMDTClTo85l7Xe40WSkoJ+shzPrX7OISlVWLdE1mKC57PSD+/LfAbdhJmvKixINBw2ESK6w==} + '@firebase/auth@1.11.1': + resolution: {integrity: sha512-Mea0G/BwC1D0voSG+60Ylu3KZchXAFilXQ/hJXWCw3gebAu+RDINZA0dJMNeym7HFxBaBaByX8jSa7ys5+F2VA==} engines: {node: '>=20.0.0'} peerDependencies: '@firebase/app': 0.x @@ -1973,8 +2303,8 @@ packages: resolution: {integrity: sha512-wR9En2A+WESUHexjmRHkqtaVH94WLNKt6rmeqZhSLBybg4Wyf0Umk04SZsS6sBq4102ZsDBFwoqMqJYj2IoDSg==} engines: {node: '>=20.0.0'} - '@firebase/data-connect@0.3.11': - resolution: {integrity: sha512-G258eLzAD6im9Bsw+Qm1Z+P4x0PGNQ45yeUuuqe5M9B1rn0RJvvsQCRHXgE52Z+n9+WX1OJd/crcuunvOGc7Vw==} + '@firebase/data-connect@0.3.12': + resolution: {integrity: sha512-baPddcoNLj/+vYo+HSJidJUdr5W4OkhT109c5qhR8T1dJoZcyJpkv/dFpYlw/VJ3dV66vI8GHQFrmAZw/xUS4g==} peerDependencies: '@firebase/app': 0.x @@ -2136,8 +2466,8 @@ packages: resolution: {integrity: sha512-IJn+8A3QZJfe7FUtWqHVNo3xJs7KFpurCWGWCiCz3oEh+BkRymKZ1QxfAbU2yGMDzTytLGQ2IV6T2r3cuo75/w==} engines: {node: '>=18'} - '@google/genai@1.26.0': - resolution: {integrity: sha512-cy5y9RgN4jBK8zr+ePgZd0To1HDpzpjIgSM6aRCZnvYR+JupGtgc1SkkOCCi1MNZho7/MuKKdnQTLhhP8OQNvg==} + '@google/genai@1.30.0': + resolution: {integrity: sha512-3MRcgczBFbUat1wIlZoLJ0vCCfXgm7Qxjh59cZi2X08RgWLtm9hKOspzp7TOg1TV2e26/MLxR2GR5yD5GmBV2w==} engines: {node: '>=20.0.0'} peerDependencies: '@modelcontextprotocol/sdk': ^1.20.1 @@ -2145,8 +2475,8 @@ packages: '@modelcontextprotocol/sdk': optional: true - '@grpc/grpc-js@1.14.0': - resolution: {integrity: sha512-N8Jx6PaYzcTRNzirReJCtADVoq4z7+1KQ4E70jTg/koQiMoUSN1kbNjPOqpPbhMFhfU1/l7ixspPl8dNY+FoUg==} + '@grpc/grpc-js@1.14.1': + resolution: {integrity: sha512-sPxgEWtPUR3EnRJCEtbGZG2iX8LQDUls2wUS3o27jg07KqJFMq6YDeWvMo1wfpmy3rqRdS0rivpLwhqQtEyCuQ==} engines: {node: '>=12.10.0'} '@grpc/grpc-js@1.9.15': @@ -2182,12 +2512,16 @@ packages: resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@inquirer/ansi@1.0.1': - resolution: {integrity: sha512-yqq0aJW/5XPhi5xOAL1xRCpe1eh8UFVgYFpFsjEqmIR8rKLyP+HINvFXwUaxYICflJrVlxnp7lLN6As735kVpw==} + '@inquirer/ansi@1.0.2': + resolution: {integrity: sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==} engines: {node: '>=18'} - '@inquirer/checkbox@4.3.0': - resolution: {integrity: sha512-5+Q3PKH35YsnoPTh75LucALdAxom6xh5D1oeY561x4cqBuH24ZFVyFREPe14xgnrtmGu3EEt1dIi60wRVSnGCw==} + '@inquirer/ansi@2.0.1': + resolution: {integrity: sha512-QAZUk6BBncv/XmSEZTscd8qazzjV3E0leUMrEPjxCd51QBgCKmprUGLex5DTsNtURm7LMzv+CLcd6S86xvBfYg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/checkbox@4.3.2': + resolution: {integrity: sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2195,6 +2529,15 @@ packages: '@types/node': optional: true + '@inquirer/checkbox@5.0.1': + resolution: {integrity: sha512-5VPFBK8jKdsjMK3DTFOlbR0+Kkd4q0AWB7VhWQn6ppv44dr3b7PU8wSJQTC5oA0f/aGW7v/ZozQJAY9zx6PKig==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/confirm@5.1.19': resolution: {integrity: sha512-wQNz9cfcxrtEnUyG5PndC8g3gZ7lGDBzmWiXZkX8ot3vfZ+/BLjR8EvyGX4YzQLeVqtAlY/YScZpW7CW8qMoDQ==} engines: {node: '>=18'} @@ -2204,8 +2547,17 @@ packages: '@types/node': optional: true - '@inquirer/core@10.3.0': - resolution: {integrity: sha512-Uv2aPPPSK5jeCplQmQ9xadnFx2Zhj9b5Dj7bU6ZeCdDNNY11nhYy4btcSdtDguHqCT2h5oNeQTcUNSGGLA7NTA==} + '@inquirer/confirm@6.0.1': + resolution: {integrity: sha512-wD+pM7IxLn1TdcQN12Q6wcFe5VpyCuh/I2sSmqO5KjWH2R4v+GkUToHb+PsDGobOe1MtAlXMwGNkZUPc2+L6NA==} + 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'} peerDependencies: '@types/node': '>=18' @@ -2213,8 +2565,17 @@ packages: '@types/node': optional: true - '@inquirer/editor@4.2.21': - resolution: {integrity: sha512-MjtjOGjr0Kh4BciaFShYpZ1s9400idOdvQ5D7u7lE6VztPFoyLcVNE5dXBmEEIQq5zi4B9h2kU+q7AVBxJMAkQ==} + '@inquirer/core@11.0.1': + resolution: {integrity: sha512-Tpf49h50e4KYffVUCXzkx4gWMafUi3aDQDwfVAAGBNnVcXiwJIj4m2bKlZ7Kgyf6wjt1eyXH1wDGXcAokm4Ssw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/editor@4.2.23': + resolution: {integrity: sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2222,8 +2583,17 @@ packages: '@types/node': optional: true - '@inquirer/expand@4.0.21': - resolution: {integrity: sha512-+mScLhIcbPFmuvU3tAGBed78XvYHSvCl6dBiYMlzCLhpr0bzGzd8tfivMMeqND6XZiaZ1tgusbUHJEfc6YzOdA==} + '@inquirer/editor@5.0.1': + resolution: {integrity: sha512-zDKobHI7Ry++4noiV9Z5VfYgSVpPZoMApviIuGwLOMciQaP+dGzCO+1fcwI441riklRiZg4yURWyEoX0Zy2zZw==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/expand@4.0.23': + resolution: {integrity: sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2231,8 +2601,17 @@ packages: '@types/node': optional: true - '@inquirer/external-editor@1.0.2': - resolution: {integrity: sha512-yy9cOoBnx58TlsPrIxauKIFQTiyH+0MK4e97y4sV9ERbI+zDxw7i2hxHLCIEGIE/8PPvDxGhgzIOTSOWcs6/MQ==} + '@inquirer/expand@5.0.1': + resolution: {integrity: sha512-TBrTpAB6uZNnGQHtSEkbvJZIQ3dXZOrwqQSO9uUbwct3G2LitwBCE5YZj98MbQ5nzihzs5pRjY1K9RRLH4WgoA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/external-editor@1.0.3': + resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2240,12 +2619,25 @@ packages: '@types/node': optional: true - '@inquirer/figures@1.0.14': - resolution: {integrity: sha512-DbFgdt+9/OZYFM+19dbpXOSeAstPy884FPy1KjDu4anWwymZeOYhMY1mdFri172htv6mvc/uvIAAi7b7tvjJBQ==} + '@inquirer/external-editor@2.0.1': + resolution: {integrity: sha512-BPYWJXCAK9w6R+pb2s3WyxUz9ts9SP/LDOUwA9fu7LeuyYgojz83i0DSRwezu736BgMwz14G63Xwj70hSzHohQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.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.2.5': - resolution: {integrity: sha512-7GoWev7P6s7t0oJbenH0eQ0ThNdDJbEAEtVt9vsrYZ9FulIokvd823yLyhQlWHJPGce1wzP53ttfdCZmonMHyA==} + '@inquirer/figures@2.0.1': + resolution: {integrity: sha512-KtMxyjLCuDFqAWHmCY9qMtsZ09HnjMsm8H3OvpSIpfhHdfw3/AiGWHNrfRwbyvHPtOJpumm8wGn5fkhtvkWRsg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + + '@inquirer/input@4.3.1': + resolution: {integrity: sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2253,8 +2645,17 @@ packages: '@types/node': optional: true - '@inquirer/number@3.0.21': - resolution: {integrity: sha512-5QWs0KGaNMlhbdhOSCFfKsW+/dcAVC2g4wT/z2MCiZM47uLgatC5N20kpkDQf7dHx+XFct/MJvvNGy6aYJn4Pw==} + '@inquirer/input@5.0.1': + resolution: {integrity: sha512-cEhEUohCpE2BCuLKtFFZGp4Ief05SEcqeAOq9NxzN5ThOQP8Rl5N/Nt9VEDORK1bRb2Sk/zoOyQYfysPQwyQtA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/number@3.0.23': + resolution: {integrity: sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2262,8 +2663,17 @@ packages: '@types/node': optional: true - '@inquirer/password@4.0.21': - resolution: {integrity: sha512-xxeW1V5SbNFNig2pLfetsDb0svWlKuhmr7MPJZMYuDnCTkpVBI+X/doudg4pznc1/U+yYmWFFOi4hNvGgUo7EA==} + '@inquirer/number@4.0.1': + resolution: {integrity: sha512-4//zgBGHe8Q/FfCoUXZUrUHyK/q5dyqiwsePz3oSSPSmw1Ijo35ZkjaftnxroygcUlLYfXqm+0q08lnB5hd49A==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.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'} peerDependencies: '@types/node': '>=18' @@ -2271,6 +2681,15 @@ packages: '@types/node': optional: true + '@inquirer/password@5.0.1': + resolution: {integrity: sha512-UJudHpd7Ia30Q+x+ctYqI9Nh6SyEkaBscpa7J6Ts38oc1CNSws0I1hJEdxbQBlxQd65z5GEJPM4EtNf6tzfWaQ==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@inquirer/prompts@7.9.0': resolution: {integrity: sha512-X7/+dG9SLpSzRkwgG5/xiIzW0oMrV3C0HOa7YHG1WnrLK+vCQHfte4k/T80059YBdei29RBC3s+pSMvPJDU9/A==} engines: {node: '>=18'} @@ -2280,8 +2699,17 @@ packages: '@types/node': optional: true - '@inquirer/rawlist@4.1.9': - resolution: {integrity: sha512-AWpxB7MuJrRiSfTKGJ7Y68imYt8P9N3Gaa7ySdkFj1iWjr6WfbGAhdZvw/UnhFXTHITJzxGUI9k8IX7akAEBCg==} + '@inquirer/prompts@8.0.1': + resolution: {integrity: sha512-MURRu/cyvLm9vchDDaVZ9u4p+ADnY0Mz3LQr0KTgihrrvuKZlqcWwlBC4lkOMvd0KKX4Wz7Ww9+uA7qEpQaqjg==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/rawlist@4.1.11': + resolution: {integrity: sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2289,8 +2717,17 @@ packages: '@types/node': optional: true - '@inquirer/search@3.2.0': - resolution: {integrity: sha512-a5SzB/qrXafDX1Z4AZW3CsVoiNxcIYCzYP7r9RzrfMpaLpB+yWi5U8BWagZyLmwR0pKbbL5umnGRd0RzGVI8bQ==} + '@inquirer/rawlist@5.0.1': + resolution: {integrity: sha512-vVfVHKUgH6rZmMlyd0jOuGZo0Fw1jfcOqZF96lMwlgavx7g0x7MICe316bV01EEoI+c68vMdbkTTawuw3O+Fgw==} + 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'} peerDependencies: '@types/node': '>=18' @@ -2298,8 +2735,17 @@ packages: '@types/node': optional: true - '@inquirer/select@4.4.0': - resolution: {integrity: sha512-kaC3FHsJZvVyIjYBs5Ih8y8Bj4P/QItQWrZW22WJax7zTN+ZPXVGuOM55vzbdCP9zKUiBd9iEJVdesujfF+cAA==} + '@inquirer/search@4.0.1': + resolution: {integrity: sha512-XwiaK5xBvr31STX6Ji8iS3HCRysBXfL/jUbTzufdWTS6LTGtvDQA50oVETt1BJgjKyQBp9vt0VU6AmU/AnOaGA==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.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'} peerDependencies: '@types/node': '>=18' @@ -2307,8 +2753,17 @@ packages: '@types/node': optional: true - '@inquirer/type@3.0.9': - resolution: {integrity: sha512-QPaNt/nmE2bLGQa9b7wwyRJoLZ7pN6rcyXvzU0YCmivmJyq1BVo94G98tStRWkoD1RgDX5C+dPlhhHzNdu/W/w==} + '@inquirer/select@5.0.1': + resolution: {integrity: sha512-gPByrgYoezGyKMq5KjV7Tuy1JU2ArIy6/sI8sprw0OpXope3VGQwP5FK1KD4eFFqEhKu470Dwe6/AyDPmGRA0Q==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + + '@inquirer/type@3.0.10': + resolution: {integrity: sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==} engines: {node: '>=18'} peerDependencies: '@types/node': '>=18' @@ -2316,6 +2771,15 @@ packages: '@types/node': optional: true + '@inquirer/type@4.0.1': + resolution: {integrity: sha512-odO8YwoQAw/eVu/PSPsDDVPmqO77r/Mq7zcoF5VduVqIu2wSRWUgmYb5K9WH1no0SjLnOe8MDKtDL++z6mfo2g==} + engines: {node: '>=23.5.0 || ^22.13.0 || ^21.7.0 || ^20.12.0'} + peerDependencies: + '@types/node': '>=18' + peerDependenciesMeta: + '@types/node': + optional: true + '@isaacs/balanced-match@4.0.1': resolution: {integrity: sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==} engines: {node: 20 || >=22} @@ -2608,20 +3072,16 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@npmcli/agent@3.0.0': - resolution: {integrity: sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q==} - engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/agent@4.0.0': resolution: {integrity: sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==} engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/fs@4.0.0': - resolution: {integrity: sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==} - engines: {node: ^18.17.0 || >=20.5.0} + '@npmcli/fs@5.0.0': + resolution: {integrity: sha512-7OsC1gNORBEawOa5+j2pXN9vsicaIOH5cPXxoR6fJOmH6/EXpJB2CajXOu1fPRFun2m1lktEFX11+P89hqO/og==} + engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/git@7.0.0': - resolution: {integrity: sha512-vnz7BVGtOctJAIHouCJdvWBhsTVSICMeUgZo2c7XAi5d5Rrl80S1H7oPym7K03cRuinK5Q6s2dw36+PgXQTcMA==} + '@npmcli/git@7.0.1': + resolution: {integrity: sha512-+XTFxK2jJF/EJJ5SoAzXk3qwIDfvFc5/g+bD274LZ7uY7LE8sTfG6Z8rOanPl2ZEvZWqNvmEdtXC25cE54VcoA==} engines: {node: ^20.17.0 || >=22.9.0} '@npmcli/installed-package-contents@3.0.0': @@ -2629,74 +3089,78 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} hasBin: true - '@npmcli/node-gyp@4.0.0': - resolution: {integrity: sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA==} - engines: {node: ^18.17.0 || >=20.5.0} + '@npmcli/node-gyp@5.0.0': + resolution: {integrity: sha512-uuG5HZFXLfyFKqg8QypsmgLQW7smiRjVc45bqD/ofZZcR/uxEjgQU8qDPv0s9TEeMUiAAU/GC5bR6++UdTirIQ==} + engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/package-json@7.0.1': - resolution: {integrity: sha512-956YUeI0YITbk2+KnirCkD19HLzES0habV+Els+dyZaVsaM6VGSiNwnRu6t3CZaqDLz4KXy2zx+0N/Zy6YjlAA==} + '@npmcli/package-json@7.0.4': + resolution: {integrity: sha512-0wInJG3j/K40OJt/33ax47WfWMzZTm6OQxB9cDhTt5huCP2a9g2GnlsxmfN+PulItNPIpPrZ+kfwwUil7eHcZQ==} engines: {node: ^20.17.0 || >=22.9.0} '@npmcli/promise-spawn@8.0.3': resolution: {integrity: sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==} engines: {node: ^18.17.0 || >=20.5.0} - '@npmcli/redact@3.2.2': - resolution: {integrity: sha512-7VmYAmk4csGv08QzrDKScdzn11jHPFGyqJW39FyPgPuAp3zIaUmuCo1yxw9aGs+NEJuTGQ9Gwqpt93vtJubucg==} - engines: {node: ^18.17.0 || >=20.5.0} + '@npmcli/promise-spawn@9.0.1': + resolution: {integrity: sha512-OLUaoqBuyxeTqUvjA3FZFiXUfYC1alp3Sa99gW3EUDz3tZ3CbXDdcZ7qWKBzicrJleIgucoWamWH1saAmH/l2Q==} + engines: {node: ^20.17.0 || >=22.9.0} + + '@npmcli/redact@4.0.0': + resolution: {integrity: sha512-gOBg5YHMfZy+TfHArfVogwgfBeQnKbbGo3pSUyK/gSI0AVu+pEiDVcKlQb0D8Mg1LNRZILZ6XG8I5dJ4KuAd9Q==} + engines: {node: ^20.17.0 || >=22.9.0} - '@npmcli/run-script@10.0.0': - resolution: {integrity: sha512-vaQj4nccJbAslopIvd49pQH2NhUp7G9pY4byUtmwhe37ZZuubGrx0eB9hW2F37uVNRuDDK6byFGXF+7JCuMSZg==} + '@npmcli/run-script@10.0.3': + resolution: {integrity: sha512-ER2N6itRkzWbbtVmZ9WKaWxVlKlOeBFF1/7xx+KA5J1xKa4JjUwBdb6tDpk0v1qA+d+VDwHI9qmLcXSWcmi+Rw==} engines: {node: ^20.17.0 || >=22.9.0} - '@octokit/auth-app@8.1.1': - resolution: {integrity: sha512-yW9YUy1cuqWlz8u7908ed498wJFt42VYsYWjvepjojM4BdZSp4t+5JehFds7LfvYi550O/GaUI94rgbhswvxfA==} + '@octokit/auth-app@8.1.2': + resolution: {integrity: sha512-db8VO0PqXxfzI6GdjtgEFHY9tzqUql5xMFXYA12juq8TeTgPAuiiP3zid4h50lwlIP457p5+56PnJOgd2GGBuw==} engines: {node: '>= 20'} - '@octokit/auth-oauth-app@9.0.2': - resolution: {integrity: sha512-vmjSHeuHuM+OxZLzOuoYkcY3OPZ8erJ5lfswdTmm+4XiAKB5PmCk70bA1is4uwSl/APhRVAv4KHsgevWfEKIPQ==} + '@octokit/auth-oauth-app@9.0.3': + resolution: {integrity: sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==} engines: {node: '>= 20'} - '@octokit/auth-oauth-device@8.0.2': - resolution: {integrity: sha512-KW7Ywrz7ei7JX+uClWD2DN1259fnkoKuVdhzfpQ3/GdETaCj4Tx0IjvuJrwhP/04OhcMu5yR6tjni0V6LBihdw==} + '@octokit/auth-oauth-device@8.0.3': + resolution: {integrity: sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==} engines: {node: '>= 20'} - '@octokit/auth-oauth-user@6.0.1': - resolution: {integrity: sha512-vlKsL1KUUPvwXpv574zvmRd+/4JiDFXABIZNM39+S+5j2kODzGgjk7w5WtiQ1x24kRKNaE7v9DShNbw43UA3Hw==} + '@octokit/auth-oauth-user@6.0.2': + resolution: {integrity: sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==} engines: {node: '>= 20'} '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} - '@octokit/core@7.0.5': - resolution: {integrity: sha512-t54CUOsFMappY1Jbzb7fetWeO0n6K0k/4+/ZpkS+3Joz8I4VcvY9OiEBFRYISqaI2fq5sCiPtAjRDOzVYG8m+Q==} + '@octokit/core@7.0.6': + resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} engines: {node: '>= 20'} - '@octokit/endpoint@11.0.1': - resolution: {integrity: sha512-7P1dRAZxuWAOPI7kXfio88trNi/MegQ0IJD3vfgC3b+LZo1Qe6gRJc2v0mz2USWWJOKrB2h5spXCzGbw+fAdqA==} + '@octokit/endpoint@11.0.2': + resolution: {integrity: sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==} engines: {node: '>= 20'} - '@octokit/graphql-schema@15.26.0': - resolution: {integrity: sha512-SoVbh+sXe9nsoweFbLT3tAk3XWYbYLs5ku05wij1zhyQ2U3lewdrhjo5Tb7lfaOGWNHSkPZT4uuPZp8neF7P7A==} + '@octokit/graphql-schema@15.26.1': + resolution: {integrity: sha512-RFDC2MpRBd4AxSRvUeBIVeBU7ojN/SxDfALUd7iVYOSeEK3gZaqR2MGOysj4Zh2xj2RY5fQAUT+Oqq7hWTraMA==} - '@octokit/graphql@9.0.2': - resolution: {integrity: sha512-iz6KzZ7u95Fzy9Nt2L8cG88lGRMr/qy1Q36ih/XVzMIlPDMYwaNLE/ENhqmIzgPrlNWiYJkwmveEetvxAgFBJw==} + '@octokit/graphql@9.0.3': + resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} engines: {node: '>= 20'} '@octokit/oauth-authorization-url@8.0.0': resolution: {integrity: sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==} engines: {node: '>= 20'} - '@octokit/oauth-methods@6.0.1': - resolution: {integrity: sha512-xi6Iut3izMCFzXBJtxxJehxJmAKjE8iwj6L5+raPRwlTNKAbOOBJX7/Z8AF5apD4aXvc2skwIdOnC+CQ4QuA8Q==} + '@octokit/oauth-methods@6.0.2': + resolution: {integrity: sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==} engines: {node: '>= 20'} - '@octokit/openapi-types@26.0.0': - resolution: {integrity: sha512-7AtcfKtpo77j7Ts73b4OWhOZHTKo/gGY8bB3bNBQz4H+GRSWqx2yvj8TXRsbdTE0eRmYmXOEY66jM7mJ7LzfsA==} + '@octokit/openapi-types@27.0.0': + resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} - '@octokit/plugin-paginate-rest@13.2.1': - resolution: {integrity: sha512-Tj4PkZyIL6eBMYcG/76QGsedF0+dWVeLhYprTmuFVVxzDW7PQh23tM0TP0z+1MvSkxB29YFZwnUX+cXfTiSdyw==} + '@octokit/plugin-paginate-rest@14.0.0': + resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=6' @@ -2707,26 +3171,26 @@ packages: peerDependencies: '@octokit/core': '>=6' - '@octokit/plugin-rest-endpoint-methods@16.1.1': - resolution: {integrity: sha512-VztDkhM0ketQYSh5Im3IcKWFZl7VIrrsCaHbDINkdYeiiAsJzjhS2xRFCSJgfN6VOcsoW4laMtsmf3HcNqIimg==} + '@octokit/plugin-rest-endpoint-methods@17.0.0': + resolution: {integrity: sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=6' - '@octokit/request-error@7.0.1': - resolution: {integrity: sha512-CZpFwV4+1uBrxu7Cw8E5NCXDWFNf18MSY23TdxCBgjw1tXXHvTrZVsXlW8hgFTOLw8RQR1BBrMvYRtuyaijHMA==} + '@octokit/request-error@7.1.0': + resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} - '@octokit/request@10.0.5': - resolution: {integrity: sha512-TXnouHIYLtgDhKo+N6mXATnDBkV05VwbR0TtMWpgTHIoQdRQfCSzmy/LGqR1AbRMbijq/EckC/E3/ZNcU92NaQ==} + '@octokit/request@10.0.7': + resolution: {integrity: sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==} engines: {node: '>= 20'} - '@octokit/rest@22.0.0': - resolution: {integrity: sha512-z6tmTu9BTnw51jYGulxrlernpsQYXpui1RK21vmXn8yF5bp6iX16yfTtJYGK5Mh1qDkvDOmp2n8sRMcQmR8jiA==} + '@octokit/rest@22.0.1': + resolution: {integrity: sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==} engines: {node: '>= 20'} - '@octokit/types@15.0.1': - resolution: {integrity: sha512-sdiirM93IYJ9ODDCBgmRPIboLbSkpLa5i+WLuXH8b8Atg+YMLAyLvDDhNWLV4OYd08tlvYfVm/dw88cqHWtw1Q==} + '@octokit/types@16.0.0': + resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} '@open-draft/deferred-promise@2.2.0': resolution: {integrity: sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==} @@ -2741,24 +3205,24 @@ packages: resolution: {integrity: sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==} engines: {node: '>=8.0.0'} - '@opentelemetry/context-async-hooks@2.1.0': - resolution: {integrity: sha512-zOyetmZppnwTyPrt4S7jMfXiSX9yyfF0hxlA8B5oo2TtKl+/RGCy7fi4DrBfIf3lCPrkKsRBWZZD7RFojK7FDg==} + '@opentelemetry/context-async-hooks@2.2.0': + resolution: {integrity: sha512-qRkLWiUEZNAmYapZ7KGS5C4OmBLcP/H2foXeOEaowYCR0wi89fHejrfYfbuLVCMLp/dWZXKvQusdbUEZjERfwQ==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/core@2.1.0': - resolution: {integrity: sha512-RMEtHsxJs/GiHHxYT58IY57UXAQTuUnZVco6ymDEqTNlJKTimM4qPUPVe8InNFyBjhHBEAx4k3Q8LtNayBsbUQ==} + '@opentelemetry/core@2.2.0': + resolution: {integrity: sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==} engines: {node: ^18.19.0 || >=20.6.0} peerDependencies: '@opentelemetry/api': '>=1.0.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.37.0': - resolution: {integrity: sha512-JD6DerIKdJGmRp4jQyX5FlrQjA4tjOw1cvfsPAZXfOOEErMUHjPcPSICS+6WnM0nB0efSFARh0KAZss+bvExOA==} + '@opentelemetry/semantic-conventions@1.38.0': + resolution: {integrity: sha512-kocjix+/sSggfJhwXqClZ3i9Y/MI0fp7b+g7kCRm6psy2dsf8uApTRclwG18h8Avm7C9+fnt+O36PspJ/OzoWg==} engines: {node: '>=14'} - '@oxc-project/types@0.95.0': - resolution: {integrity: sha512-vACy7vhpMPhjEJhULNxrdR0D943TkA/MigMpJCHmBHvMXxRStRi/dPtTlfQ3uDwWSzRpT8z+7ImjZVf8JWBocQ==} + '@oxc-project/types@0.96.0': + resolution: {integrity: sha512-r/xkmoXA0xEpU6UGtn18CNVjXH6erU3KCpCDbpLmbVxBFor1U9MqN5Z2uMmCHJuXjJzlnDR+hWY+yPoLo8oHDw==} '@parcel/watcher-android-arm64@2.5.1': resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} @@ -2860,16 +3324,16 @@ packages: resolution: {integrity: sha512-tNe7a6U4rCpxLMBaR0SIYTdjxGdL0Vwb3G1zY8++sPtHSvy7qd54u8CIB0Z+Y6t5tc9pNYMYCMwhE/wdSY7ltg==} engines: {node: '>=18.12'} - '@pnpm/dependency-path@1001.1.3': - resolution: {integrity: sha512-ScPXDrlpNNrvV+l4Z1Mh7HjejkltQWfSa3PIaB+WJ3h+PoC1w5blbgfq6ENdHdkRU7L14ie/3MqUGMIx2gZldA==} + '@pnpm/dependency-path@1001.1.5': + resolution: {integrity: sha512-powgYgNzuAdrZK+bx1Vxes5LRFp8ByUCcFsCeo0pQpyFbKpRDFF31FUVSE3CGs61WgL0lTBQr7ZoUSRc+BDrCw==} engines: {node: '>=18.12'} '@pnpm/graceful-fs@1000.0.1': resolution: {integrity: sha512-JnzaAVFJIEgwTcB55eww8N3h5B6qJdZqDA2wYkSK+OcTvvMSQb9c2STMhBP6GfkWygG1fs3w8D7JRx9SPZnxJg==} engines: {node: '>=18.12'} - '@pnpm/types@1000.9.0': - resolution: {integrity: sha512-UvDTCxnbyqkTg2X0dBOuZ4IdFJ8g4UFu0Ybv/5/cZAxCWVhNl1hC/Xc9hR4tZrlBL0NRFePLRhO/iw9LmA1lbw==} + '@pnpm/types@1001.0.1': + resolution: {integrity: sha512-v5X09E6LkJFOOw9FgGITpAs7nQJtx6u3N0SNtyIC5mSeIC5SebMrrelpCz6QUTJvyXBEa1AWj2dZhYfLj59xhA==} engines: {node: '>=18.12'} '@protobufjs/aspromise@1.1.2': @@ -2902,100 +3366,100 @@ packages: '@protobufjs/utf8@1.1.0': resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} - '@puppeteer/browsers@2.10.12': - resolution: {integrity: sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw==} + '@puppeteer/browsers@2.10.13': + resolution: {integrity: sha512-a9Ruw3j3qlnB5a/zHRTkruppynxqaeE4H9WNj5eYGRWqw0ZauZ23f4W2ARf3hghF5doozyD+CRtt7XSYuYRI/Q==} engines: {node: '>=18'} hasBin: true - '@rolldown/binding-android-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-g9ejDOehJFhxC1DIXQuZQ9bKv4lRDioOTL42cJjFjqKPl1L7DVb9QQQE1FxokGEIMr6FezLipxwnzOXWe7DNPg==} + '@rolldown/binding-android-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-vPP9/MZzESh9QtmvQYojXP/midjgkkc1E4AdnPPAzQXo668ncHJcVLKjJKzoBdsQmaIvNjrMdsCwES8vTQHRQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-PxAW1PXLPmCzfhfKIS53kwpjLGTUdIfX4Ht+l9mj05C3lYCGaGowcNsYi2rdxWH24vSTmeK+ajDNRmmmrK0M7g==} + '@rolldown/binding-darwin-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-Lc3nrkxeaDVCVl8qR3qoxh6ltDZfkQ98j5vwIr5ALPkgjZtDK4BGCrrBoLpGVMg+csWcaqUbwbKwH5yvVa0oOw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.0-beta.44': - resolution: {integrity: sha512-/CtQqs1oO9uSb5Ju60rZvsdjE7Pzn8EK2ISAdl2jedjMzeD/4neNyCbwyJOAPzU+GIQTZVyrFZJX+t7HXR1R/g==} + '@rolldown/binding-darwin-x64@1.0.0-beta.47': + resolution: {integrity: sha512-eBYxQDwP0O33plqNVqOtUHqRiSYVneAknviM5XMawke3mwMuVlAsohtOqEjbCEl/Loi/FWdVeks5WkqAkzkYWQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': - resolution: {integrity: sha512-V5Q5W9c4+2GJ4QabmjmVV6alY97zhC/MZBaLkDtHwGy3qwzbM4DYgXUbun/0a8AH5hGhuU27tUIlYz6ZBlvgOA==} + '@rolldown/binding-freebsd-x64@1.0.0-beta.47': + resolution: {integrity: sha512-Ns+kgp2+1Iq/44bY/Z30DETUSiHY7ZuqaOgD5bHVW++8vme9rdiWsN4yG4rRPXkdgzjvQ9TDHmZZKfY4/G11AA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': - resolution: {integrity: sha512-X6adjkHeFqKsTU0FXdNN9HY4LDozPqIfHcnXovE5RkYLWIjMWuc489mIZ6iyhrMbCqMUla9IOsh5dvXSGT9o9A==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47': + resolution: {integrity: sha512-4PecgWCJhTA2EFOlptYJiNyVP2MrVP4cWdndpOu3WmXqWqZUmSubhb4YUAIxAxnXATlGjC1WjxNPhV7ZllNgdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-kRRKGZI4DXWa6ANFr3dLA85aSVkwPdgXaRjfanwY84tfc3LncDiIjyWCb042e3ckPzYhHSZ3LmisO+cdOIYL6Q==} + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47': + resolution: {integrity: sha512-CyIunZ6D9U9Xg94roQI1INt/bLkOpPsZjZZkiaAZ0r6uccQdICmC99M9RUPlMLw/qg4yEWLlQhG73W/mG437NA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-hMtiN9xX1NhxXBa2U3Up4XkVcsVp2h73yYtMDY59z9CDLEZLrik9RVLhBL5QtoX4zZKJ8HZKJtWuGYvtmkCbIQ==} + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47': + resolution: {integrity: sha512-doozc/Goe7qRCSnzfJbFINTHsMktqmZQmweull6hsZZ9sjNWQ6BWQnbvOlfZJe4xE5NxM1NhPnY5Giqnl3ZrYQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': - resolution: {integrity: sha512-rd1LzbpXQuR8MTG43JB9VyXDjG7ogSJbIkBpZEHJ8oMKzL6j47kQT5BpIXrg3b5UVygW9QCI2fpFdMocT5Kudg==} + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.47': + resolution: {integrity: sha512-fodvSMf6Aqwa0wEUSTPewmmZOD44rc5Tpr5p9NkwQ6W1SSpUKzD3SwpJIgANDOhwiYhDuiIaYPGB7Ujkx1q0UQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': - resolution: {integrity: sha512-qI2IiPqmPRW25exXkuQr3TlweCDc05YvvbSDRPCuPsWkwb70dTiSoXn8iFxT4PWqTi71wWHg1Wyta9PlVhX5VA==} + '@rolldown/binding-linux-x64-musl@1.0.0-beta.47': + resolution: {integrity: sha512-Rxm5hYc0mGjwLh5sjlGmMygxAaV2gnsx7CNm2lsb47oyt5UQyPDZf3GP/ct8BEcwuikdqzsrrlIp8+kCSvMFNQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': - resolution: {integrity: sha512-+vHvEc1pL5iJRFlldLC8mjm6P4Qciyfh2bh5ZI6yxDQKbYhCHRKNURaKz1mFcwxhVL5YMYsLyaqM3qizVif9MQ==} + '@rolldown/binding-openharmony-arm64@1.0.0-beta.47': + resolution: {integrity: sha512-YakuVe+Gc87jjxazBL34hbr8RJpRuFBhun7NEqoChVDlH5FLhLXjAPHqZd990TVGVNkemourf817Z8u2fONS8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': - resolution: {integrity: sha512-XSgLxRrtFj6RpTeMYmmQDAwHjKseYGKUn5LPiIdW4Cq+f5SBSStL2ToBDxkbdxKPEbCZptnLPQ/nfKcAxrC8Xg==} + '@rolldown/binding-wasm32-wasi@1.0.0-beta.47': + resolution: {integrity: sha512-ak2GvTFQz3UAOw8cuQq8pWE+TNygQB6O47rMhvevvTzETh7VkHRFtRUwJynX5hwzFvQMP6G0az5JrBGuwaMwYQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-cF1LJdDIX02cJrFrX3wwQ6IzFM7I74BYeKFkzdcIA4QZ0+2WA7/NsKIgjvrunupepWb1Y6PFWdRlHSaz5AW1Wg==} + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-o5BpmBnXU+Cj+9+ndMcdKjhZlPb79dVPBZnWwMnI4RlNSSq5yOvFZqvfPYbyacvnW03Na4n5XXQAPhu3RydZ0w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-5uaJonDafhHiMn+iEh7qUp3QQ4Gihv3lEOxKfN8Vwadpy0e+5o28DWI42DpJ9YBYMrVy4JOWJ/3etB/sptpUwA==} + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-FVOmfyYehNE92IfC9Kgs913UerDog2M1m+FADJypKz0gmRg3UyTt4o1cZMCAl7MiR89JpM9jegNO1nXuP1w1vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': - resolution: {integrity: sha512-vsqhWAFJkkmgfBN/lkLCWTXF1PuPhMjfnAyru48KvF7mVh2+K7WkKYHezF3Fjz4X/mPScOcIv+g6cf6wnI6eWg==} + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47': + resolution: {integrity: sha512-by/70F13IUE101Bat0oeH8miwWX5mhMFPk1yjCdxoTNHTyTdLgb0THNaebRM6AP7Kz+O3O2qx87sruYuF5UxHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@rolldown/pluginutils@1.0.0-beta.44': - resolution: {integrity: sha512-g6eW7Zwnr2c5RADIoqziHoVs6b3W5QTQ4+qbpfjbkMJ9x+8Og211VW/oot2dj9dVwaK/UyC6Yo+02gV+wWQVNg==} + '@rolldown/pluginutils@1.0.0-beta.47': + resolution: {integrity: sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==} '@rollup/plugin-alias@5.1.1': resolution: {integrity: sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ==} @@ -3006,8 +3470,8 @@ packages: rollup: optional: true - '@rollup/plugin-commonjs@28.0.8': - resolution: {integrity: sha512-o1Ug9PxYsF61R7/NXO/GgMZZproLd/WH2XA53Tp9ppf6bU1lMlTtC/gUM6zM3mesi2E0rypk+PNtVrELREyWEQ==} + '@rollup/plugin-commonjs@28.0.9': + resolution: {integrity: sha512-PIR4/OHZ79romx0BVVll/PkwWpJ7e5lsqFa3gFfcrFPWwLXLV39JVUzQV9RKjWerE7B845Hqjj9VYlQeieZ2dA==} engines: {node: '>=16.0.0 || 14 >= 14.17'} peerDependencies: rollup: ^2.68.0||^3.0.0||^4.0.0 @@ -3060,119 +3524,59 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.52.4': - resolution: {integrity: sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==} - cpu: [arm] - os: [android] - '@rollup/rollup-android-arm-eabi@4.52.5': resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.52.4': - resolution: {integrity: sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==} - cpu: [arm64] - os: [android] - '@rollup/rollup-android-arm64@4.52.5': resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.52.4': - resolution: {integrity: sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==} - cpu: [arm64] - os: [darwin] - '@rollup/rollup-darwin-arm64@4.52.5': resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.52.4': - resolution: {integrity: sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==} - cpu: [x64] - os: [darwin] - '@rollup/rollup-darwin-x64@4.52.5': resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.52.4': - resolution: {integrity: sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==} - cpu: [arm64] - os: [freebsd] - '@rollup/rollup-freebsd-arm64@4.52.5': resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.4': - resolution: {integrity: sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==} - cpu: [x64] - os: [freebsd] - '@rollup/rollup-freebsd-x64@4.52.5': resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': - resolution: {integrity: sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==} - cpu: [arm] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.52.4': - resolution: {integrity: sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==} - cpu: [arm] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm-musleabihf@4.52.5': resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.52.4': - resolution: {integrity: sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==} - cpu: [arm64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-gnu@4.52.5': resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.52.4': - resolution: {integrity: sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==} - cpu: [arm64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-musl@4.52.5': resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==} - cpu: [arm64] - os: [linux] - libc: [musl] - - '@rollup/rollup-linux-loong64-gnu@4.52.4': - resolution: {integrity: sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==} - cpu: [loong64] + cpu: [arm64] os: [linux] - libc: [glibc] + libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.5': resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==} @@ -3180,130 +3584,69 @@ packages: os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.52.4': - resolution: {integrity: sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==} - cpu: [ppc64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-gnu@4.52.5': resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.52.4': - resolution: {integrity: sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==} - cpu: [riscv64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-gnu@4.52.5': resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.52.4': - resolution: {integrity: sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==} - cpu: [riscv64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-musl@4.52.5': resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.52.4': - resolution: {integrity: sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==} - cpu: [s390x] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-s390x-gnu@4.52.5': resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.52.4': - resolution: {integrity: sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==} - cpu: [x64] - os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.52.5': resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.52.4': - resolution: {integrity: sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==} - cpu: [x64] - os: [linux] - libc: [musl] - '@rollup/rollup-linux-x64-musl@4.52.5': resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openharmony-arm64@4.52.4': - resolution: {integrity: sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==} - cpu: [arm64] - os: [openharmony] - '@rollup/rollup-openharmony-arm64@4.52.5': resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.52.4': - resolution: {integrity: sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==} - cpu: [arm64] - os: [win32] - '@rollup/rollup-win32-arm64-msvc@4.52.5': resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.4': - resolution: {integrity: sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==} - cpu: [ia32] - os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.52.5': resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.4': - resolution: {integrity: sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-gnu@4.52.5': resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.4': - resolution: {integrity: sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==} - cpu: [x64] - os: [win32] - '@rollup/rollup-win32-x64-msvc@4.52.5': resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==} cpu: [x64] os: [win32] - '@rollup/wasm-node@4.52.5': - resolution: {integrity: sha512-ldY4tEzSMBHNwB8TfRpi7RRRjjyfKlwjdebw5pS1lu0xaY3g4RDc6ople2wEYulVOKVeH7ZJwRx0iw4pGtjMHg==} + '@rollup/wasm-node@4.53.3': + resolution: {integrity: sha512-mB8z32H6kz4kVjn+tfTGcrXBae7rIeAvm/g6itsE3IqcXpjSRRvk1/EOWDEi5wL8NNmxXiH71t4jtNfr128zpw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3344,8 +3687,8 @@ packages: '@standard-schema/spec@1.0.0': resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} - '@stylistic/eslint-plugin@5.5.0': - resolution: {integrity: sha512-IeZF+8H0ns6prg4VrkhgL+yrvDXWDH2cKchrbh80ejG9dQgZWp10epHMbgRuQvgchLII/lfh6Xn3lu6+6L86Hw==} + '@stylistic/eslint-plugin@5.6.1': + resolution: {integrity: sha512-JCs+MqoXfXrRPGbGmho/zGS/jMcn3ieKl/A8YImqib76C8kjgZwq5uUFzc30lJkMvcchuRn6/v8IApLxli3Jyw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: '>=9.0.0' @@ -3361,8 +3704,8 @@ packages: '@tootallnate/quickjs-emscripten@0.23.0': resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -3411,11 +3754,11 @@ packages: '@types/bonjour@3.5.13': resolution: {integrity: sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==} - '@types/browser-sync@2.29.0': - resolution: {integrity: sha512-d2V8FDX/LbDCSm343N2VChzDxvll0h76I8oSigYpdLgPDmcdcR6fywTggKBkUiDM3qAbHOq7NZvepj/HJM5e2g==} + '@types/browser-sync@2.29.1': + resolution: {integrity: sha512-jAMsEkLpNURfpS4XIN9BX7SY+uCoTkPjLIovwssV/3e/FPwg9hYusbCXmGNfC3T6W/6d3iP3clxy9cvevjFKtQ==} - '@types/chai@5.2.2': - resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/cli-progress@3.11.6': resolution: {integrity: sha512-cE3+jb9WRlu+uOSAugewNpITJDt1VF8dHOopPO4IABFc3SXYL5WE/+PTz/FCdZRRfIujiWW3n3aMbv1eIGVRWA==} @@ -3438,8 +3781,8 @@ packages: '@types/convert-source-map@2.0.3': resolution: {integrity: sha512-ag0BfJLZf6CQz8VIuRIEYQ5Ggwk/82uvTQf27RcpyDNbY0Vw49LIPqAxk5tqYfrCs9xDaIMvl4aj7ZopnYL8bA==} - '@types/cookies@0.9.1': - resolution: {integrity: sha512-E/DPgzifH4sM1UMadJMWd6mO2jOd4g1Ejwzx8/uRCDpJis1IrlyQEcGAYEomtAqRYmD5ORbNXMeI9U0RiVGZbg==} + '@types/cookies@0.9.2': + resolution: {integrity: sha512-1AvkDdZM2dbyFybL4fxpuNCaWyv//0AwsuUk2DWeXyM1/5ZKm6W3z6mQi24RZ4l2ucY+bkSHzbDVpySqPGuV8A==} '@types/cors@2.8.19': resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} @@ -3450,8 +3793,8 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/duplexify@3.6.4': - resolution: {integrity: sha512-2eahVPsd+dy3CL6FugAzJcxoraWhUghZGEQJns1kTKfCXWKJ5iG/VkaB05wRVrDKHfOFKqb0X0kXh91eE99RZg==} + '@types/duplexify@3.6.5': + resolution: {integrity: sha512-fB56ACzlW91UdZ5F3VXplVMDngO8QaX5Y2mjvADtN01TT2TMy4WjF0Lg+tFDvt4uMBeTe4SgaD+qCrA7dL5/tA==} '@types/ejs@3.1.5': resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} @@ -3474,17 +3817,17 @@ packages: '@types/express-serve-static-core@5.1.0': resolution: {integrity: sha512-jnHMsrd0Mwa9Cf4IdOzbz543y4XJepXrbia2T4b6+spXC2We3t1y6K44D3mR8XMFSXMCf3/l7rCgddfx7UNVBA==} - '@types/express@4.17.23': - resolution: {integrity: sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==} + '@types/express@4.17.25': + resolution: {integrity: sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==} - '@types/express@5.0.3': - resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} + '@types/express@5.0.5': + resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==} '@types/folder-hash@4.0.4': resolution: {integrity: sha512-c+PwHm51Dw3fXM8SDK+93PO3oXdk4XNouCCvV67lj4aijRkZz5g67myk+9wqWWnyv3go6q96hT6ywcd3XtoZiQ==} - '@types/git-raw-commits@5.0.0': - resolution: {integrity: sha512-MQIzbZxgEnKpN1kCcw9JlQIu3Wdw5c4CCCP2cUli+DYgFjzsjtGLOeUe8oqPjjrKJudOoFnNuIZb/4sYHXEWZg==} + '@types/git-raw-commits@5.0.1': + resolution: {integrity: sha512-sd4kgxJbuZF0RDy6cX7KlKSGiwqB1mqn8nriUbxt5e1F+MO/N4hJlhaYn0Omw4g2biClFpT5Mre07x7OkGt8tg==} '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -3495,8 +3838,8 @@ packages: '@types/http-errors@2.0.5': resolution: {integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==} - '@types/http-proxy@1.17.16': - resolution: {integrity: sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==} + '@types/http-proxy@1.17.17': + resolution: {integrity: sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw==} '@types/ini@4.1.1': resolution: {integrity: sha512-MIyNUZipBTbyUNnhvuXJTY7B6qNI78meck9Jbv3wk0OgNwRyOOVEKDutAkOs1snB/tx0FafyR6/SN4Ps0hZPeg==} @@ -3513,8 +3856,8 @@ packages: '@types/jasmine-reporters@2.5.3': resolution: {integrity: sha512-8aojAUdgdiD9VQbllBJb/9gny3lOjz9T5gyMcbYlKe6npwGVsarbr8v2JYSFJSZSuFYXcPVzFG2lLX3ib0j/DA==} - '@types/jasmine@5.1.12': - resolution: {integrity: sha512-1BzPxNsFDLDfj9InVR3IeY0ZVf4o9XV+4mDqoCfyPkbsA7dYyKAPAb2co6wLFlHcvxPlt1wShm7zQdV7uTfLGA==} + '@types/jasmine@5.1.13': + resolution: {integrity: sha512-MYCcDkruFc92LeYZux5BC0dmqo2jk+M5UIZ4/oFnAPCXN9mCcQhLyj7F3/Za7rocVyt5YRr1MmqJqFlvQ9LVcg==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -3528,8 +3871,8 @@ packages: '@types/keygrip@1.0.6': resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==} - '@types/koa-compose@3.2.8': - resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} + '@types/koa-compose@3.2.9': + resolution: {integrity: sha512-BroAZ9FTvPiCy0Pi8tjD1OfJ7bgU1gQf0eR6e1Vm+JJATy9eKOG3hQMFtMciMawiSOVnLMdmUOC46s7HBhSTsA==} '@types/koa@2.15.0': resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} @@ -3540,8 +3883,8 @@ packages: '@types/loader-utils@3.0.0': resolution: {integrity: sha512-oOi4OGpiLUbb+Q/cN9FIkkDFgOpOGZ2cUAzb5i03wrGstnG6Syx1WDMhSiB5rcP10XX7cw7Uev8mv++/aplnNg==} - '@types/lodash@4.17.20': - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} + '@types/lodash@4.17.21': + resolution: {integrity: sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==} '@types/micromatch@2.3.35': resolution: {integrity: sha512-J749bHo/Zu56w0G0NI/IGHLQPiSsjx//0zJhfEVAN95K/xM5C8ZDmhkXtU3qns0sBOao7HuQzr8XV1/2o5LbXA==} @@ -3555,17 +3898,17 @@ packages: '@types/node-forge@1.3.14': resolution: {integrity: sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==} - '@types/node@22.18.11': - resolution: {integrity: sha512-Gd33J2XIrXurb+eT2ktze3rJAfAp9ZNjlBdh4SVgyrKEOADwCbdUDaK7QgJno8Ue4kcajscsKqu6n8OBG3hhCQ==} + '@types/node@22.19.1': + resolution: {integrity: sha512-LCCV0HdSZZZb34qifBsyWlUmok6W7ouER+oQIGBScS8EsZsQbrtFTUrDX4hOl+CS6p7cnNC4td+qrSVGSCTUfQ==} - '@types/node@24.9.1': - resolution: {integrity: sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==} + '@types/node@24.10.1': + resolution: {integrity: sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==} '@types/npm-package-arg@6.1.4': resolution: {integrity: sha512-vDgdbMy2QXHnAruzlv68pUtXCjmqUk3WrBAsRboRovsOmxbfn/WiYCjmecyKjGztnMps5dWp4Uq2prp+Ilo17Q==} - '@types/npm-registry-fetch@8.0.8': - resolution: {integrity: sha512-VL/chssZawBkaQ5gFD5njblJce/ny9OICBlWAG9X6/m/ypPNJMWYiM22SY2mhLIGoknd4AyEJyi+FGyrBnsr+A==} + '@types/npm-registry-fetch@8.0.9': + resolution: {integrity: sha512-7NxvodR5Yrop3pb6+n8jhJNyzwOX0+6F+iagNEoi9u1CGxruYAwZD8pvGc9prIkL0+FdX5Xp0p80J9QPrGUp/g==} '@types/npmlog@7.0.0': resolution: {integrity: sha512-hJWbrKFvxKyWwSUXjZMYTINsSOY6IclhvGOZ97M8ac2tmR9hMwmTnYaMdpGhvju9ctWLTPhCS+eLfQNluiEjQQ==} @@ -3585,8 +3928,8 @@ packages: '@types/progress@2.0.7': resolution: {integrity: sha512-iadjw02vte8qWx7U0YM++EybBha2CQLPGu9iJ97whVgJUT5Zq9MjAPYUnbfRI2Kpehimf1QjFJYxD0t8nqzu5w==} - '@types/pumpify@1.4.4': - resolution: {integrity: sha512-+cWbQUecD04MQYkjNBhPmcUIP368aloYmqm+ImdMKA8rMpxRNAhZAD6gIj+sAVTF1DliqrT/qUp6aGNi/9U3tw==} + '@types/pumpify@1.4.5': + resolution: {integrity: sha512-BGVAQyK5yJdfIII230fVYGY47V63hUNAhryuuS3b4lEN2LNwxUXFKsEf8QLDCjmZuimlj23BHppJgcrGvNtqKg==} '@types/q@0.0.32': resolution: {integrity: sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==} @@ -3615,17 +3958,20 @@ packages: '@types/semver@7.7.1': resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==} - '@types/send@0.17.5': - resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} + '@types/send@0.17.6': + resolution: {integrity: sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==} - '@types/send@1.2.0': - resolution: {integrity: sha512-zBF6vZJn1IaMpg3xUF25VK3gd3l8zwE0ZLRX7dsQyQi+jp4E8mMDJNGDYnYse+bQhYwWERTxVwHpi3dMOq7RKQ==} + '@types/send@1.2.1': + resolution: {integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==} '@types/serve-index@1.9.4': resolution: {integrity: sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==} - '@types/serve-static@1.15.9': - resolution: {integrity: sha512-dOTIuqpWLyl3BBXU3maNQsS4A3zuuoYRNIvYSxxhebPfXg2mzWQEPne/nlJ37yOse6uGgR386uTpdsx4D0QZWA==} + '@types/serve-static@1.15.10': + resolution: {integrity: sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==} + + '@types/serve-static@2.2.0': + resolution: {integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==} '@types/sockjs@0.3.36': resolution: {integrity: sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==} @@ -3636,8 +3982,8 @@ packages: '@types/stack-trace@0.0.33': resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==} - '@types/watchpack@2.4.4': - resolution: {integrity: sha512-SbuSavsPxfOPZwVHBgQUVuzYBe6+8KL7dwiJLXaj5rmv3DxktOMwX5WP1J6UontwUbewjVoc7pCgZvqy6rPn+A==} + '@types/watchpack@2.4.5': + resolution: {integrity: sha512-8CarnGOIYYRL342jwQyHrGwz4vCD3y5uwwYmzQVzT2Z24DqSd6wwBva6m0eNJX4S5pVmrx9xUEbOsOoqBVhWsg==} '@types/which@3.0.4': resolution: {integrity: sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==} @@ -3651,8 +3997,8 @@ packages: '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} '@types/yarnpkg__lockfile@1.1.9': resolution: {integrity: sha512-GD4Fk15UoP5NLCNor51YdfL9MSdldKCqOC9EssrRw3HVfar9wUZ5y8Lfnp+qVD6hIinLr8ygklDYnmlnlQo12Q==} @@ -3666,20 +4012,20 @@ packages: peerDependencies: '@typescript-eslint/parser': ^8.46.2 eslint: ^8.57.0 || ^9.0.0 - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/parser@8.46.2': resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/project-service@8.46.2': resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/scope-manager@8.46.2': resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} @@ -3689,35 +4035,35 @@ packages: resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/type-utils@8.46.2': resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: 5.9.3 - - '@typescript-eslint/types@8.46.1': - resolution: {integrity: sha512-C+soprGBHwWBdkDpbaRC4paGBrkIXxVlNohadL5o0kfhsXqOC6GYH2S/Obmig+I0HTDl8wMaRySwrfrXVP8/pQ==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/types@8.46.2': resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.48.0': + resolution: {integrity: sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.46.2': resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/utils@8.46.2': resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: 5.9.3 + typescript: '>=4.8.4 <6.0.0' '@typescript-eslint/visitor-keys@8.46.2': resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} @@ -3808,20 +4154,20 @@ packages: peerDependencies: vite: ^6.0.0 || ^7.0.0 - '@vitest/coverage-v8@4.0.0': - resolution: {integrity: sha512-VyVmZ8TccaGCZT9C0/vIi3kbPmvD/rLUcRx1AC8QzqaCm9SLYr1p3rxirW3EuwEGWnVU7KjGckAwJR1SRtgurA==} + '@vitest/coverage-v8@4.0.8': + resolution: {integrity: sha512-wQgmtW6FtPNn4lWUXi8ZSYLpOIb92j3QCujxX3sQ81NTfQ/ORnE0HtK7Kqf2+7J9jeveMGyGyc4NWc5qy3rC4A==} peerDependencies: - '@vitest/browser': 4.0.0 - vitest: 4.0.0 + '@vitest/browser': 4.0.8 + vitest: 4.0.8 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.0.0': - resolution: {integrity: sha512-NLwsOv2m6RfTEMk5AhpFIUVbd5BDmZnev5XxIIwJiNsXFOetFdqMzil/paGpwwbfQyaeQCokB1rQbKsnvLeR/w==} + '@vitest/expect@4.0.8': + resolution: {integrity: sha512-Rv0eabdP/xjAHQGr8cjBm+NnLHNoL268lMDK85w2aAGLFoVKLd8QGnVon5lLtkXQCoYaNL0wg04EGnyKkkKhPA==} - '@vitest/mocker@4.0.0': - resolution: {integrity: sha512-s5S729mda0Umb60zbZeyYm58dpv97VNOXZ1bLSZ9AfaOE8TJoW4IDfEnw3IaCk9nq/Hug80hFmAz5NAh+XOImQ==} + '@vitest/mocker@4.0.8': + resolution: {integrity: sha512-9FRM3MZCedXH3+pIh+ME5Up2NBBHDq0wqwhOKkN4VnvCiKbVxddqH9mSGPZeawjd12pCOGnl+lo/ZGHt0/dQSg==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0-0 @@ -3831,20 +4177,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.0.0': - resolution: {integrity: sha512-oUjxwO6VcUP0VtCkJERILS2yKV4AZiE1VTWDjvjeb8pXG6P5iubyeP+cmcj2vzCPdUst8vNhXMqC1CnxvDN97Q==} + '@vitest/pretty-format@4.0.8': + resolution: {integrity: sha512-qRrjdRkINi9DaZHAimV+8ia9Gq6LeGz2CgIEmMLz3sBDYV53EsnLZbJMR1q84z1HZCMsf7s0orDgZn7ScXsZKg==} - '@vitest/runner@4.0.0': - resolution: {integrity: sha512-w3kADT0nDmY4dQyfPtq7zEe6wbwDy88Go2b7NpWuj0iqA1H26CTS/JB2/t8tKbvxk7MTJ9vTsRK/VMVuKmLPaQ==} + '@vitest/runner@4.0.8': + resolution: {integrity: sha512-mdY8Sf1gsM8hKJUQfiPT3pn1n8RF4QBcJYFslgWh41JTfrK1cbqY8whpGCFzBl45LN028g0njLCYm0d7XxSaQQ==} - '@vitest/snapshot@4.0.0': - resolution: {integrity: sha512-ELrK8qhbH3WdhD/2qh3NnR7xnaxOGx62NYLj5XKAGPIABOc+1ITN1XfH/MTgdP6Ov7O91DycuGrzwpizdCpuHg==} + '@vitest/snapshot@4.0.8': + resolution: {integrity: sha512-Nar9OTU03KGiubrIOFhcfHg8FYaRaNT+bh5VUlNz8stFhCZPNrJvmZkhsr1jtaYvuefYFwK2Hwrq026u4uPWCw==} - '@vitest/spy@4.0.0': - resolution: {integrity: sha512-VKD9p74W9ALFV2dSy3j8WtitY3gtloO+U6EZq84TY5gTaTTt1Lvs9nZnuaBomzEHYp/QbtGRMMKBOCsir2IAgA==} + '@vitest/spy@4.0.8': + resolution: {integrity: sha512-nvGVqUunyCgZH7kmo+Ord4WgZ7lN0sOULYXUOYuHr55dvg9YvMz3izfB189Pgp28w0vWFbEEfNc/c3VTrqrXeA==} - '@vitest/utils@4.0.0': - resolution: {integrity: sha512-8OXfn18Y7UtcpqhiQAfxcmZIsemPPKcg/guU2CLvafXn50G6B2EvKuj3A5h7ZMAvm95tDkll13rTtdd08MKjLQ==} + '@vitest/utils@4.0.8': + resolution: {integrity: sha512-pdk2phO5NDvEFfUTxcTP8RFYjVj/kfLSPIN5ebP2Mu9kcIMeAQTbknqcFEyBcC4z2pJlJI9aS5UQjcYfhmKAow==} '@web/browser-logs@0.4.1': resolution: {integrity: sha512-ypmMG+72ERm+LvP+loj9A64MTXvWMXHUOu773cPO4L1SV/VWg6xA9Pv7vkvkXQX+ItJtCJt+KQ+U6ui2HhSFUw==} @@ -3958,9 +4304,9 @@ packages: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true - abbrev@3.0.1: - resolution: {integrity: sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg==} - engines: {node: ^18.17.0 || >=20.5.0} + abbrev@4.0.0: + resolution: {integrity: sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==} + engines: {node: ^20.17.0 || >=22.9.0} abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} @@ -4048,8 +4394,8 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-escapes@7.1.1: - resolution: {integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==} + ansi-escapes@7.2.0: + resolution: {integrity: sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==} engines: {node: '>=18'} ansi-html-community@0.0.8: @@ -4165,12 +4511,16 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + ast-types@0.13.4: resolution: {integrity: sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==} engines: {node: '>=4'} - ast-v8-to-istanbul@0.3.7: - resolution: {integrity: sha512-kr1Hy6YRZBkGQSb6puP+D6FQ59Cx4m0siYhAxygMCAgadiWQ6oxAxQXHOMvJx67SJ63jRoVIIg5eXzUbbct1ww==} + ast-v8-to-istanbul@0.3.8: + resolution: {integrity: sha512-szgSZqUxI5T8mLKvS7WTjF9is+MVbOeLADU73IseOcrqhxr/VAvy6wfoVE39KnKzA7JRhjF5eUagNlHwvZPlKQ==} astral-regex@2.0.0: resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} @@ -4247,16 +4597,16 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - bare-events@2.8.0: - resolution: {integrity: sha512-AOhh6Bg5QmFIXdViHbMc2tLDsBIRxdkIaIddPslJF9Z5De3APBScuqGP2uThXnIpqFrgoxMNC6km7uXNIMLHXA==} + bare-events@2.8.2: + resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: bare-abort-controller: '*' peerDependenciesMeta: bare-abort-controller: optional: true - bare-fs@4.4.11: - resolution: {integrity: sha512-Bejmm9zRMvMTRoHS+2adgmXw1ANZnCNx+B5dgZpGwlP1E3x6Yuxea8RToddHUbWtVV0iUMWqsgZr8+jcgUI2SA==} + bare-fs@4.5.1: + resolution: {integrity: sha512-zGUCsm3yv/ePt2PHNbVxjjn0nNB1MkIaR4wOCxJ2ig5pCf5cCVAYJXVhQg/3OhhJV6DB1ts7Hv0oUaElc2TPQg==} engines: {bare: '>=1.16.0'} peerDependencies: bare-buffer: '*' @@ -4282,8 +4632,8 @@ packages: bare-events: optional: true - bare-url@2.3.0: - resolution: {integrity: sha512-c+RCqMSZbkz97Mw1LWR0gcOqwK82oyYKfLoHJ8k13ybi1+I80ffdDzUy0TdAburdrR/kI0/VuN8YgEnJqX+Nyw==} + bare-url@2.3.2: + resolution: {integrity: sha512-ZMq4gd9ngV5aTMa5p9+UfY0b3skwhHELaDkhEHetMdX0LRkW9kzaym4oo/Eh+Ghm0CCDuMTsRIGM/ytUc1ZYmw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -4292,8 +4642,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.8.18: - resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==} + baseline-browser-mapping@2.8.31: + resolution: {integrity: sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==} hasBin: true basic-ftp@5.0.5: @@ -4344,8 +4694,8 @@ packages: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - body-parser@2.2.0: - resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==} + body-parser@2.2.1: + resolution: {integrity: sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==} engines: {node: '>=18'} bonjour-service@1.3.0: @@ -4382,8 +4732,8 @@ packages: browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} - browserslist@4.26.3: - resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==} + browserslist@4.28.0: + resolution: {integrity: sha512-tbydkR/CxfMwelN0vwdP/pLkDwyAASZ+VfWm4EOwlB6SWhx1sYnWLqo8N5j0rAzPfzfRaxt0mM/4wPU/Su84RQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -4420,12 +4770,8 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} - cacache@19.0.1: - resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - cacache@20.0.1: - resolution: {integrity: sha512-+7LYcYGBYoNqTp1Rv7Ny1YjUo5E0/ftkQtraH3vkfAGgVHc+ouWdC8okAwQgQR7EVIdW6JTzTmhKFwzb+4okAQ==} + cacache@20.0.3: + resolution: {integrity: sha512-3pUp4e8hv07k1QlijZu6Kn7c9+ZpWWk4j3F8N3xPuCExULobqJydKYOTj1FTq58srkJsXvO7LbGAH4C0ZU3WGw==} engines: {node: ^20.17.0 || >=22.9.0} cache-content-type@1.0.1: @@ -4464,14 +4810,14 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001751: - resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + caniuse-lite@1.0.30001757: + resolution: {integrity: sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==} caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} - chai@6.2.0: - resolution: {integrity: sha512-aUTnJc/JipRzJrNADXVvpVqi6CO0dn3nx4EVPxijri+fj3LUUDyZQOgVeW54Ob3Y1Xh9Iz8f+CgaCl8v0mn9bA==} + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} engines: {node: '>=18'} chalk-template@0.4.0: @@ -4490,8 +4836,8 @@ packages: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - chardet@2.1.0: - resolution: {integrity: sha512-bNFETTG/pM5ryzQ9Ad0lJOTa6HWD/YsScAR3EnCPZRPlQh77JocYktSHOUHelyhm8IARL+o4c4F1bP5KVOjiRA==} + chardet@2.1.1: + resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} checkpoint-stream@0.1.2: resolution: {integrity: sha512-eYXIcydL3mPjjEVLxHdi1ISgTwmxGJZ8vyJ3lYVvFTDRyTOZMTbKZdRJqiA7Gi1rPcwOyyzcrZmGLL8ff7e69w==} @@ -4520,8 +4866,8 @@ packages: resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} engines: {node: '>=6.0'} - chromium-bidi@9.1.0: - resolution: {integrity: sha512-rlUzQ4WzIAWdIbY/viPShhZU2n21CxDUgazXVbw4Hu1MwaeUSEksSeM6DqPgpRjCLXRk702AVRxJxoOz0dw4OA==} + chromium-bidi@11.0.0: + resolution: {integrity: sha512-cM3DI+OOb89T3wO8cpPSro80Q9eKYJ7hGVXoGS3GkDPxnYSqiv+6xwpIf6XERyJ9Tdsl09hmNmY94BkgZdVekw==} peerDependencies: devtools-protocol: '*' @@ -4541,8 +4887,8 @@ packages: resolution: {integrity: sha512-/+40ljC3ONVnYIttjMWrlL51nItDAbBrq2upN8BPyvGU/2n5Oxw3tbNwORCaNuNqLJnxGqOfjUuhsv7l5Q4IsQ==} engines: {node: '>=18.20'} - cli-truncate@5.1.0: - resolution: {integrity: sha512-7JDGG+4Zp0CsknDCedl0DYdaeOhc46QNpXi3NLQblkZpXXgA6LncLDUUyvrjSvZeF3VRQa+KiMGomazQrC1V8g==} + cli-truncate@5.1.1: + resolution: {integrity: sha512-SroPvNHxUnk+vIW/dOSfNqdy1sPEFkrTk6TUtqLCnBlo3N7TNYYkzzN7uSD6+jVjrdO4+p8nH7JzH6cIvUem6A==} engines: {node: '>=20'} cli-width@4.1.0: @@ -4614,8 +4960,8 @@ packages: resolution: {integrity: sha512-PqMLy5+YGwhMh1wS04mVG44oqDsgyLRSKJBdOo1bnYhMKBW65gZF1dRp2OZRhiTjgUHljy99qkO7bsctLaw35Q==} engines: {node: '>=12.20.0'} - commander@14.0.1: - resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} + commander@14.0.2: + resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} commander@2.20.3: @@ -4662,9 +5008,9 @@ packages: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} - content-disposition@1.0.0: - resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} - engines: {node: '>= 0.6'} + content-disposition@1.0.1: + resolution: {integrity: sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==} + engines: {node: '>=18'} content-type@1.0.5: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} @@ -4713,8 +5059,8 @@ packages: peerDependencies: webpack: ^5.1.0 - core-js-compat@3.46.0: - resolution: {integrity: sha512-p9hObIIEENxSV8xIu+V68JjSeARg6UVMG5mR+JEUguG3sI6MsiS1njz2jHmyJDvA+8jX/sytkBHup6kxhM9law==} + core-js-compat@3.47.0: + resolution: {integrity: sha512-IGfuznZ/n7Kp9+nypamBhvwdwLsW6KC8IOaURw2doAK5e98AG3acVLdh0woOnEqCfUtS+Vu882JE4k/DAm3ItQ==} core-util-is@1.0.2: resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} @@ -4730,7 +5076,7 @@ packages: resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} engines: {node: '>=14'} peerDependencies: - typescript: 5.9.3 + typescript: '>=4.9.5' peerDependenciesMeta: typescript: optional: true @@ -4776,8 +5122,8 @@ packages: engines: {node: '>=4'} hasBin: true - cssstyle@5.3.1: - resolution: {integrity: sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==} + cssstyle@5.3.3: + resolution: {integrity: sha512-OytmFH+13/QXONJcC75QNdMtKpceNk3u8ThBjyyYjkEcy/ekBwR1mMAuNvi3gdBPW3N5TlCzQ0WZw8H0lN/bDw==} engines: {node: '>=20'} custom-event@1.0.1: @@ -4903,12 +5249,12 @@ packages: resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} engines: {node: '>=0.10.0'} - default-browser-id@5.0.0: - resolution: {integrity: sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==} + default-browser-id@5.0.1: + resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} - default-browser@5.2.1: - resolution: {integrity: sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==} + default-browser@5.4.0: + resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} engines: {node: '>=18'} default-gateway@6.0.3: @@ -4993,8 +5339,8 @@ packages: devtools-protocol@0.0.1045489: resolution: {integrity: sha512-D+PTmWulkuQW4D1NTiCRCFxF7pQPn0hgp4YyX4wAQ6xYXKOadSWPR3ENGDQ47MW/Ewc9v2rpC/UEEGahgBYpSQ==} - devtools-protocol@0.0.1508733: - resolution: {integrity: sha512-QJ1R5gtck6nDcdM+nlsaJXcelPEI7ZxSMw1ujHpO1c4+9l+Nue5qlebi9xO1Z2MGr92bFOQTW7/rrheh5hHxDg==} + devtools-protocol@0.0.1521046: + resolution: {integrity: sha512-vhE6eymDQSKWUXwwA37NtTTVEzjtGVfDr3pRbsWEQ5onH/Snp2c+2xZHWJJawG/0hCCJLRGt4xVtEVUVILol4w==} di@0.0.1: resolution: {integrity: sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA==} @@ -5070,8 +5416,8 @@ packages: engines: {node: '>=0.10.0'} hasBin: true - electron-to-chromium@1.5.237: - resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==} + electron-to-chromium@1.5.260: + resolution: {integrity: sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} @@ -5190,13 +5536,23 @@ packages: es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - esbuild-wasm@0.25.11: - resolution: {integrity: sha512-60gllbYFIRGzB6KALBB5Va9Wy3VeCi2U0NgmM7r+TFnRgzeEyoCn2D7fhacW2zWbd7MUeTKLDE7RlfYGBQ00bw==} + esbuild-wasm@0.26.0: + resolution: {integrity: sha512-9rZuermDo9ZbWvKBv/vDRaRciCpR4L3rEbZLDs5kDq3TrCHRQZaQipQeV9wK/btpLBzNUBujTrd1uorDxbL/GA==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + esbuild@0.26.0: + resolution: {integrity: sha512-3Hq7jri+tRrVWha+ZeIVhl4qJRha/XjRNSopvTsOaCvfPHrflTYTcUFcEjMKdxofsXXsdc4zjg5NOTnL4Gl57Q==} engines: {node: '>=18'} hasBin: true - esbuild@0.25.11: - resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==} + esbuild@0.27.0: + resolution: {integrity: sha512-jd0f4NHbD6cALCyGElNpGAOtWxSq46l9X/sWB0Nzd5er4Kz2YTm+Vl0qKFT9KUJvD8+fiO8AvoHhFvEatfVixA==} engines: {node: '>=18'} hasBin: true @@ -5498,8 +5854,8 @@ packages: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} - firebase@12.4.0: - resolution: {integrity: sha512-/chNgDQ6ppPPGOQO4jctxOa/5JeQxuhaxA7Y90K0I+n/wPfoO8mRveedhVUdo7ExLcWUivnnow/ouSLYSI5Icw==} + firebase@12.6.0: + resolution: {integrity: sha512-8ZD1Gcv916Qp8/nsFH2+QMIrfX/76ti6cJwxQUENLXXnKlOX/IJZaU2Y3bdYf5r1mbownrQKfnWtrt+MVgdwLA==} flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} @@ -5544,8 +5900,8 @@ packages: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} engines: {node: '>= 0.12'} - form-data@4.0.4: - resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} formdata-polyfill@4.0.10: @@ -5599,12 +5955,12 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} - gaxios@7.1.2: - resolution: {integrity: sha512-/Szrn8nr+2TsQT1Gp8iIe/BEytJmbyfrbFh419DfGQSkEgNEhbPi7JRJuughjkTzPWgU9gBQf5AVu3DbHt0OXA==} + gaxios@7.1.3: + resolution: {integrity: sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==} engines: {node: '>=18'} - gcp-metadata@8.1.1: - resolution: {integrity: sha512-dTCcAe9fRQf06ELwel6lWWFrEbstwjUBYEhr5VRGoC+iPDZQucHppCowaIp8b8v92tU1G4X4H3b/Y6zXZxkMsQ==} + gcp-metadata@8.1.2: + resolution: {integrity: sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==} engines: {node: '>=18'} generator-function@2.0.1: @@ -5647,8 +6003,8 @@ packages: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} - get-tsconfig@4.12.0: - resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==} + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} get-uri@6.0.5: resolution: {integrity: sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==} @@ -5679,14 +6035,13 @@ packages: glob-to-regexp@0.4.1: resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} - glob@10.4.5: - resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + glob@10.5.0: + resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} hasBin: true - glob@11.0.3: - resolution: {integrity: sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==} + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} engines: {node: 20 || >=22} - hasBin: true glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} @@ -5712,16 +6067,16 @@ packages: resolution: {integrity: sha512-HJRTIH2EeH44ka+LWig+EqT2ONSYpVlNfx6pyd592/VF1TbfljJ7elwie7oSwcViLGqOdWocSdu2txwBF9bjmQ==} engines: {node: '>=0.10.0'} - google-auth-library@10.4.1: - resolution: {integrity: sha512-VlvZ+QDWng3aPh++0BSQlSJyjn4qgLLTmqylAR3as0dr6YwPkZpHcZAngAFr68TDVCUSQVRTkV73K/D3m7vEIg==} + google-auth-library@10.5.0: + resolution: {integrity: sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==} engines: {node: '>=18'} - google-gax@5.0.4: - resolution: {integrity: sha512-HmQ6zIYBs2EikTk+kjeHmtHprNTEpsnVaKONw9cwZZwUNCkUb+D5RYrJpCxyjdvIDvJp3wLbVReolJLRZRms1g==} + google-gax@5.0.6: + resolution: {integrity: sha512-1kGbqVQBZPAAu4+/R1XxPQKP0ydbNYoLAr4l0ZO2bMV0kLyLW4I1gAk++qBLWt7DPORTzmWRMsCZe86gDjShJA==} engines: {node: '>=18'} - google-logging-utils@1.1.1: - resolution: {integrity: sha512-rcX58I7nqpu4mbKztFeOAObbomBbHU2oIb/d3tJfF3dizGSApqtSwYJigGCooHdnMyQBIw8BrWyK96w3YXgr6A==} + google-logging-utils@1.1.3: + resolution: {integrity: sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==} engines: {node: '>=14'} gopd@1.2.0: @@ -5744,8 +6099,8 @@ packages: peerDependencies: graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 - graphql@16.11.0: - resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} + graphql@16.12.0: + resolution: {integrity: sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} grpc-gcp@1.0.1: @@ -5852,6 +6207,10 @@ packages: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + http-parser-js@0.5.10: resolution: {integrity: sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==} @@ -6000,8 +6359,12 @@ packages: resolution: {integrity: sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw==} engines: {node: ^18.17.0 || >=20.5.0} - injection-js@2.6.0: - resolution: {integrity: sha512-uRUO2qh7rFFeAo3UWTbLHCFr8x3VLHRNZ2jbMv/MAxbFIFgw7QtNVfxc3iC7CV5U11cvIyAt12nxVWu1NqVsYg==} + ini@6.0.0: + resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==} + engines: {node: ^20.17.0 || >=22.9.0} + + injection-js@2.6.1: + resolution: {integrity: sha512-dbR5bdhi7TWDoCye9cByZqeg/gAfamm8Vu3G1KZOTYkOif8WkuM8CD0oeDPtZYMzT5YH76JAFB7bkmyY9OJi2A==} internal-ip@6.2.0: resolution: {integrity: sha512-D8WGsR6yDt8uq7vDMu7mjcR+yRMm3dW8yufyChmszWRjcSHuxLBkR3GdS2HZAjodsaGuCvXeEJpueisXJULghg==} @@ -6011,8 +6374,8 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} - ip-address@10.0.1: - resolution: {integrity: sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==} + ip-address@10.1.0: + resolution: {integrity: sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==} engines: {node: '>= 12'} ip-regex@4.3.0: @@ -6267,8 +6630,8 @@ packages: resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} engines: {node: '>= 8.0.0'} - isbinaryfile@5.0.6: - resolution: {integrity: sha512-I+NmIfBHUl+r2wcDd6JwE9yWje/PIVY/R5/CmV8dXLZd5K+L9X2klAOwfAHNnondLXkbHyTAleQAWonpTJBTtw==} + isbinaryfile@5.0.7: + resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==} engines: {node: '>= 18.0.0'} isexe@2.0.0: @@ -6316,10 +6679,6 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.1.1: - resolution: {integrity: sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==} - engines: {node: 20 || >=22} - jake@10.9.4: resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} engines: {node: '>=10'} @@ -6331,8 +6690,8 @@ packages: jasmine-core@4.6.1: resolution: {integrity: sha512-VYz/BjjmC3klLJlLwA4Kw8ytk0zDSmbbDLNs794VnWmkcCB7I9aAL/D48VNQtmITyPvea2C3jdUMfc3kAoy0PQ==} - jasmine-core@5.12.0: - resolution: {integrity: sha512-QqO4pX33GEML5JoGQU6BM5NHKPgEsg+TXp3jCIDek9MbfEp2JUYEFBo9EF1+hegWy/bCHS1m5nP0BOp18G6rVA==} + jasmine-core@5.12.1: + resolution: {integrity: sha512-P/UbRZ0LKwXe7wEpwDheuhunPwITn4oPALhrJEQJo6756EwNGnsK/TSQrWojBB4cQDQ+VaxWYws9tFNDuiMh2Q==} jasmine-reporters@2.5.2: resolution: {integrity: sha512-qdewRUuFOSiWhiyWZX8Yx3YNQ9JG51ntBEO4ekLQRpktxFTwUHy24a86zD/Oi2BRTKksEdfWQZcQFqzjqIkPig==} @@ -6373,12 +6732,16 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsbn@0.1.1: resolution: {integrity: sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==} - jsdom@27.0.1: - resolution: {integrity: sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA==} - engines: {node: '>=20'} + jsdom@27.1.0: + resolution: {integrity: sha512-Pcfm3eZ+eO4JdZCXthW9tCDT3nF4K+9dmeZ+5X39n+Kqz0DDIABRP5CAEOHRFZk8RGuC2efksTJxrjp8EXCunQ==} + engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} peerDependencies: canvas: ^3.0.0 peerDependenciesMeta: @@ -6399,9 +6762,9 @@ packages: json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} - json-parse-even-better-errors@4.0.0: - resolution: {integrity: sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA==} - engines: {node: ^18.17.0 || >=20.5.0} + json-parse-even-better-errors@5.0.0: + resolution: {integrity: sha512-ZF1nxZ28VhQouRWhUcVlUIN3qwSgPuswK05s/HIaoetAoE/9tngVmCHjSxmSQPav1nd+lPtTL0YZ/2AFdR/iYQ==} + engines: {node: ^20.17.0 || >=22.9.0} json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -6498,6 +6861,7 @@ packages: keygrip@1.1.0: resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==} engines: {node: '>= 0.6'} + deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} @@ -6528,8 +6892,8 @@ packages: resolution: {integrity: sha512-zPPuIt+ku1iCpFBRwseMcPYQ1cJL8l60rSmKeOuGfOXyE6YnTBmf2aEFNL2HQGrD0cPcLO/t+v9RTgC+fwEh/g==} engines: {node: ^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4} - launch-editor@2.11.1: - resolution: {integrity: sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==} + launch-editor@2.12.0: + resolution: {integrity: sha512-giOHXoOtifjdHqUamwKq6c49GzBdLjvxrd2D+Q4V6uOHopJv7p9VJxikDsQ/CBXZbEITgUqSVHXLTG3VhPP1Dg==} less-loader@12.3.0: resolution: {integrity: sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==} @@ -6691,8 +7055,11 @@ packages: magic-string@0.30.19: resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} - magicast@0.3.5: - resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} make-dir@2.1.0: resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} @@ -6705,12 +7072,8 @@ packages: make-error@1.3.6: resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - make-fetch-happen@14.0.3: - resolution: {integrity: sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ==} - engines: {node: ^18.17.0 || >=20.5.0} - - make-fetch-happen@15.0.2: - resolution: {integrity: sha512-sI1NY4lWlXBAfjmCtVWIIpBypbBdhHtcjnwnv+gtCnsaOffyFil3aidszGC8hgzJe+fT1qix05sWxmD/Bmf/oQ==} + make-fetch-happen@15.0.3: + resolution: {integrity: sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==} engines: {node: ^20.17.0 || >=22.9.0} marky@1.3.0: @@ -6731,8 +7094,8 @@ packages: resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} engines: {node: '>= 0.8'} - memfs@4.49.0: - resolution: {integrity: sha512-L9uC9vGuc4xFybbdOpRLoOAOq1YEBBsocCs5NVW32DfU+CZWWIn3OVF+lB8Gp4ttBVSMazwrTrjv8ussX/e3VQ==} + memfs@4.51.0: + resolution: {integrity: sha512-4zngfkVM/GpIhC8YazOsM6E8hoB33NP0BCESPOA6z7qaL6umPJNqkO8CNYaLV2FB2MV6H1O3x2luHHOSqppv+A==} meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} @@ -6772,9 +7135,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime-types@3.0.1: - resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==} - engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} @@ -6816,8 +7179,8 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - minimatch@10.0.3: - resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} engines: {node: 20 || >=22} minimatch@3.1.2: @@ -6842,9 +7205,9 @@ packages: resolution: {integrity: sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw==} engines: {node: '>=16 || 14 >=14.17'} - minipass-fetch@4.0.1: - resolution: {integrity: sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ==} - engines: {node: ^18.17.0 || >=20.5.0} + minipass-fetch@5.0.0: + resolution: {integrity: sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==} + engines: {node: ^20.17.0 || >=22.9.0} minipass-flush@1.0.5: resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} @@ -6920,6 +7283,10 @@ packages: resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==} engines: {node: ^18.17.0 || >=20.5.0} + mute-stream@3.0.0: + resolution: {integrity: sha512-dkEJPVvun4FryqBmZ5KhDo0K9iDXAwn08tMLDinNdRBNPcYEDiWYysLcc6k3mjTMlbP9KyylvRpd4wFtwrT9rw==} + engines: {node: ^20.17.0 || >=22.9.0} + nanocolors@0.2.13: resolution: {integrity: sha512-0n3mSAQLPpGLV9ORXT5+C/D4mwew7Ebws69Hx4E2sgz2ZA5+32Q80B9tL8PbL7XHnRDiAxH/pnrUJ9a4fkTNTA==} @@ -6955,15 +7322,15 @@ packages: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - ng-packagr@21.0.0-next.4: - resolution: {integrity: sha512-WDa+yU3iCjQR9/Y9R88l13Cpm0OeElT99yC3cXN6ETN23aMLjvZayma0pC+FhPnsentzYaz2tGyMRgnL/9TNNw==} + ng-packagr@21.0.0: + resolution: {integrity: sha512-2lMGkmS91FyP+p/Tzmu49hY+p1PDgHBNM+Fce8yrzZo8/EbybNPBYfJnwFfl0lwGmqpYLevH2oh12+ikKCLv9g==} engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0} hasBin: true peerDependencies: - '@angular/compiler-cli': ^21.0.0-next + '@angular/compiler-cli': ^21.0.0-next || ^21.0.0 tailwindcss: ^2.0.0 || ^3.0.0 || ^4.0.0 tslib: ^2.3.0 - typescript: 5.9.3 + typescript: '>=5.9 <6.0' peerDependenciesMeta: tailwindcss: optional: true @@ -7020,17 +7387,17 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-gyp@11.5.0: - resolution: {integrity: sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ==} - engines: {node: ^18.17.0 || >=20.5.0} + node-gyp@12.1.0: + resolution: {integrity: sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g==} + engines: {node: ^20.17.0 || >=22.9.0} hasBin: true - node-releases@2.0.25: - resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==} + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} - nopt@8.1.0: - resolution: {integrity: sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A==} - engines: {node: ^18.17.0 || >=20.5.0} + nopt@9.0.0: + resolution: {integrity: sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==} + engines: {node: ^20.17.0 || >=22.9.0} hasBin: true normalize-path@3.0.0: @@ -7049,28 +7416,32 @@ packages: resolution: {integrity: sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA==} engines: {node: ^18.17.0 || >=20.5.0} - npm-install-checks@7.1.2: - resolution: {integrity: sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==} - engines: {node: ^18.17.0 || >=20.5.0} + npm-install-checks@8.0.0: + resolution: {integrity: sha512-ScAUdMpyzkbpxoNekQ3tNRdFI8SJ86wgKZSQZdUxT+bj0wVFpsEMWnkXP0twVe1gJyNF5apBWDJhhIbgrIViRA==} + engines: {node: ^20.17.0 || >=22.9.0} npm-normalize-package-bin@4.0.0: resolution: {integrity: sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w==} engines: {node: ^18.17.0 || >=20.5.0} + npm-normalize-package-bin@5.0.0: + resolution: {integrity: sha512-CJi3OS4JLsNMmr2u07OJlhcrPxCeOeP/4xq67aWNai6TNWWbTrlNDgl8NcFKVlcBKp18GPj+EzbNIgrBfZhsag==} + engines: {node: ^20.17.0 || >=22.9.0} + npm-package-arg@13.0.1: resolution: {integrity: sha512-6zqls5xFvJbgFjB1B2U6yITtyGBjDBORB7suI4zA4T/sZ1OmkMFlaQSNB/4K0LtXNA1t4OprAFxPisadK5O2ag==} engines: {node: ^20.17.0 || >=22.9.0} - npm-packlist@10.0.2: - resolution: {integrity: sha512-DrIWNiWT0FTdDRjGOYfEEZUNe1IzaSZ+up7qBTKnrQDySpdmuOQvytrqQlpK5QrCA4IThMvL4wTumqaa1ZvVIQ==} + npm-packlist@10.0.3: + resolution: {integrity: sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==} engines: {node: ^20.17.0 || >=22.9.0} - npm-pick-manifest@11.0.1: - resolution: {integrity: sha512-HnU7FYSWbo7dTVHtK0G+BXbZ0aIfxz/aUCVLN0979Ec6rGUX5cJ6RbgVx5fqb5G31ufz+BVFA7y1SkRTPVNoVQ==} + npm-pick-manifest@11.0.3: + resolution: {integrity: sha512-buzyCfeoGY/PxKqmBqn1IUJrZnUi1VVJTdSSRPGI60tJdUhUoSQFhs0zycJokDdOznQentgrpf8LayEHyyYlqQ==} engines: {node: ^20.17.0 || >=22.9.0} - npm-registry-fetch@19.0.0: - resolution: {integrity: sha512-DFxSAemHUwT/POaXAOY4NJmEWBPB0oKbwD6FFDE9hnt1nORkt/FXvgjD4hQjoKoHw9u0Ezws9SPXwV7xE/Gyww==} + npm-registry-fetch@19.1.1: + resolution: {integrity: sha512-TakBap6OM1w0H73VZVDf44iFXsOS3h+L4wVMXmbWOQroZgFhMch0juN6XSzBNlD965yIKvWg2dfu7NSiaYLxtw==} engines: {node: ^20.17.0 || >=22.9.0} npm-run-path@4.0.1: @@ -7210,8 +7581,8 @@ packages: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} - p-map@7.0.3: - resolution: {integrity: sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==} + p-map@7.0.4: + resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} p-queue@6.6.2: @@ -7302,8 +7673,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-scurry@2.0.0: - resolution: {integrity: sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} engines: {node: 20 || >=22} path-to-regexp@0.1.12: @@ -7379,8 +7750,8 @@ packages: resolution: {integrity: sha512-0u3N7H4+hbr40KjuVn2uNhOcthu/9usKhnw5vT3J7ply79v3D3M8naI00el9Klcy16x557VsEkkUQaHCWFXC/g==} engines: {node: '>=20.x'} - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} engines: {node: '>=16.20.0'} pkg-dir@8.0.0: @@ -7467,6 +7838,10 @@ packages: resolution: {integrity: sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==} engines: {node: ^18.17.0 || >=20.5.0} + proc-log@6.0.0: + resolution: {integrity: sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==} + engines: {node: ^20.17.0 || >=22.9.0} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -7492,8 +7867,8 @@ packages: resolution: {integrity: sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==} engines: {node: '>= 8'} - proto3-json-serializer@3.0.3: - resolution: {integrity: sha512-iUi7jGLuECChuoUwtvf6eXBDcFXTHAt5GM6ckvtD3RqD+j2wW0GW6WndPOu9IWeUk7n933lzrskcNMHJy2tFSw==} + proto3-json-serializer@3.0.4: + resolution: {integrity: sha512-E1sbAYg3aEbXrq0n1ojJkRHQJGE1kaE/O6GLA94y8rnJBfgvOPTOd1b9hOceQK1FFZI9qMh1vBERCyO2ifubcw==} engines: {node: '>=18'} protobufjs@7.5.4: @@ -7543,8 +7918,8 @@ packages: resolution: {integrity: sha512-MRtTAZfQTluz3U2oU/X2VqVWPcR1+94nbA2V6ZrSZRVEwLqZ8eclZ551qGFQD/vD2PYqHJwWOW/fpC721uznVw==} engines: {node: '>=14.1.0'} - puppeteer-core@24.25.0: - resolution: {integrity: sha512-8Xs6q3Ut+C8y7sAaqjIhzv1QykGWG4gc2mEZ2mYE7siZFuRp4xQVehOf8uQKSQAkeL7jXUs3mknEeiqnRqUKvQ==} + puppeteer-core@24.31.0: + resolution: {integrity: sha512-pnAohhSZipWQoFpXuGV7xCZfaGhqcBR9C4pVrU0QSrcMi7tQMH9J9lDBqBvyMAHQqe8HCARuREqFuVKRQOgTvg==} engines: {node: '>=18'} puppeteer@18.2.1: @@ -7600,8 +7975,8 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - raw-body@3.0.1: - resolution: {integrity: sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==} + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} readable-stream@2.3.8: @@ -7749,8 +8124,12 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true - rolldown@1.0.0-beta.44: - resolution: {integrity: sha512-gcqgyCi3g93Fhr49PKvymE8PoaGS0sf6ajQrsYaQ8o5de6aUEbD6rJZiJbhOfpcqOnycgsAsUNPYri1h25NgsQ==} + rimraf@5.0.10: + resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} + hasBin: true + + rolldown@1.0.0-beta.47: + resolution: {integrity: sha512-Mid74GckX1OeFAOYz9KuXeWYhq3xkXbMziYIC+ULVdUzPTG9y70OBSBQDQn9hQP8u/AfhuYw1R0BSg15nBI4Dg==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -7763,7 +8142,7 @@ packages: engines: {node: '>=16'} peerDependencies: rollup: ^3.29.4 || ^4 - typescript: 5.9.3 + typescript: ^4.5 || ^5.0 rollup-plugin-sourcemaps2@0.5.4: resolution: {integrity: sha512-XK6ITvEsKtUFN1GQbYKoqilwh1yKxTS9BLaFlVsm0IaYUYe3eVnhBWzKP4AHbkBO2BNOheGNlf407K7wCj6Rrw==} @@ -7775,11 +8154,6 @@ packages: '@types/node': optional: true - rollup@4.52.4: - resolution: {integrity: sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==} - engines: {node: '>=18.0.0', npm: '>=8.0.0'} - hasBin: true - rollup@4.52.5: resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -7789,9 +8163,6 @@ packages: resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} engines: {node: '>= 18'} - rrweb-cssom@0.8.0: - resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} - run-applescript@7.1.0: resolution: {integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==} engines: {node: '>=18'} @@ -7859,8 +8230,8 @@ packages: saucelabs@1.5.0: resolution: {integrity: sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==} - sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + sax@1.4.3: + resolution: {integrity: sha512-yqYn1JhPczigF94DMS+shiDMjDowYO6y9+wB/4WgO0Y19jWYk0lQ4tuG5KI7kj4FTp1wxPj5IFfcrz/s1c3jjQ==} saxes@6.0.0: resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} @@ -8120,6 +8491,10 @@ packages: resolution: {integrity: sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ==} engines: {node: ^18.17.0 || >=20.5.0} + ssri@13.0.0: + resolution: {integrity: sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==} + engines: {node: ^20.17.0 || >=22.9.0} + stack-trace@0.0.10: resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} @@ -8282,8 +8657,8 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - tar@7.5.1: - resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==} + tar@7.5.2: + resolution: {integrity: sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==} engines: {node: '>=18'} teeny-request@10.1.0: @@ -8355,15 +8730,15 @@ packages: tldts-core@6.1.86: resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} - tldts-core@7.0.17: - resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==} + tldts-core@7.0.19: + resolution: {integrity: sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==} tldts@6.1.86: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - tldts@7.0.17: - resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==} + tldts@7.0.19: + resolution: {integrity: sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==} hasBin: true tmp@0.0.30: @@ -8423,7 +8798,7 @@ packages: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} peerDependencies: - typescript: 5.9.3 + typescript: '>=4.8.4' ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} @@ -8432,7 +8807,7 @@ packages: '@swc/core': '>=1.2.50' '@swc/wasm': '>=1.2.50' '@types/node': '*' - typescript: 5.9.3 + typescript: '>=2.7' peerDependenciesMeta: '@swc/core': optional: true @@ -8545,9 +8920,6 @@ packages: unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} - undici-types@6.21.0: - resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.16.0: resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} @@ -8584,13 +8956,13 @@ packages: unicode-trie@2.0.0: resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} - unique-filename@4.0.0: - resolution: {integrity: sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==} - engines: {node: ^18.17.0 || >=20.5.0} + unique-filename@5.0.0: + resolution: {integrity: sha512-2RaJTAvAb4owyjllTfXzFClJ7WsGxlykkPvCr9pA//LD9goVq+m4PPAeBgNodGZ7nSrntT/auWpJ6Y5IFXcfjg==} + engines: {node: ^20.17.0 || >=22.9.0} - unique-slug@5.0.0: - resolution: {integrity: sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==} - engines: {node: ^18.17.0 || >=20.5.0} + unique-slug@6.0.0: + resolution: {integrity: sha512-4Lup7Ezn8W3d52/xBhZBVdx323ckxa7DEvd9kPQHppTkLoJXw6ltrBCyj5pnrxj0qKDxYMJ56CoxNuFCscdTiw==} + engines: {node: ^20.17.0 || >=22.9.0} universal-github-app-jwt@2.2.2: resolution: {integrity: sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==} @@ -8609,8 +8981,8 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.1.4: + resolution: {integrity: sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -8684,8 +9056,8 @@ packages: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} - vite@7.1.11: - resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==} + vite@7.2.2: + resolution: {integrity: sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -8724,18 +9096,18 @@ packages: yaml: optional: true - vitest@4.0.0: - resolution: {integrity: sha512-Z+qKuTt2py+trSv2eJNYPaQKos88EmmLntXLAJkOHdd1v3BdcS4DgIkyC6cQPRoh8tWb+QiFfW08U347mjcV0g==} + vitest@4.0.8: + resolution: {integrity: sha512-urzu3NCEV0Qa0Y2PwvBtRgmNtxhj5t5ULw7cuKhIHh3OrkKTLlut0lnBOv9qe5OvbkMH2g38G7KPDCTpIytBVg==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@types/debug': ^4.1.12 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.0.0 - '@vitest/browser-preview': 4.0.0 - '@vitest/browser-webdriverio': 4.0.0 - '@vitest/ui': 4.0.0 + '@vitest/browser-playwright': 4.0.8 + '@vitest/browser-preview': 4.0.8 + '@vitest/browser-webdriverio': 4.0.8 + '@vitest/ui': 4.0.8 happy-dom: '*' jsdom: '*' peerDependenciesMeta: @@ -8783,8 +9155,8 @@ packages: web-vitals@4.2.4: resolution: {integrity: sha512-r4DIlprAGwJ7YM11VZp4R884m0Vmgr6EAKe3P+kO0PPj3Unqyvv59rczf6UiGcb9Z8QxZVcqKNwv/g0WNdWwsw==} - webdriver-bidi-protocol@0.3.7: - resolution: {integrity: sha512-wIx5Gu/LLTeexxilpk8WxU2cpGAKlfbWRO5h+my6EMD1k5PYqM1qQO1MHUFf4f3KRnhBvpbZU7VkizAgeSEf7g==} + webdriver-bidi-protocol@0.3.9: + resolution: {integrity: sha512-uIYvlRQ0PwtZR1EzHlTMol1G0lAlmOe6wPykF9a77AK3bkpvZHzIVxRE2ThOx5vjy2zISe0zhwf5rzuUfbo1PQ==} webdriver-js-extender@2.1.0: resolution: {integrity: sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==} @@ -8916,6 +9288,11 @@ packages: engines: {node: ^18.17.0 || >=20.5.0} hasBin: true + which@6.0.0: + resolution: {integrity: sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==} + engines: {node: ^20.17.0 || >=22.9.0} + hasBin: true + why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} @@ -9110,10 +9487,10 @@ packages: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} - zod-to-json-schema@3.24.6: - resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + zod-to-json-schema@3.25.0: + resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} peerDependencies: - zod: ^3.24.1 + zod: ^3.25 || ^4 zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -9126,6 +9503,8 @@ packages: snapshots: + '@acemir/cssom@0.9.24': {} + '@actions/core@1.11.1': dependencies: '@actions/exec': 1.1.1 @@ -9231,28 +9610,28 @@ snapshots: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 - '@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))': + '@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))': dependencies: - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 - '@angular/cdk@21.0.0-next.10(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/cdk@21.0.0(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) parse5: 8.0.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3)': + '@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3)': dependencies: - '@angular/compiler': 21.0.0-next.9 + '@angular/compiler': 21.0.1 '@babel/core': 7.28.4 '@jridgewell/sourcemap-codec': 1.5.5 chokidar: 4.0.3 @@ -9266,31 +9645,31 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/compiler@21.0.0-next.9': + '@angular/compiler@21.0.1': dependencies: tslib: 2.8.1 - '@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)': + '@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)': dependencies: rxjs: 7.8.2 tslib: 2.8.1 optionalDependencies: - '@angular/compiler': 21.0.0-next.9 + '@angular/compiler': 21.0.1 zone.js: 0.15.1 - '@angular/forms@21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2)': + '@angular/forms@21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) '@standard-schema/spec': 1.0.0 rxjs: 7.8.2 tslib: 2.8.1 - '@angular/localize@21.0.0-next.9(@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3))(@angular/compiler@21.0.0-next.9)': + '@angular/localize@21.0.1(@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3))(@angular/compiler@21.0.1)': dependencies: - '@angular/compiler': 21.0.0-next.9 - '@angular/compiler-cli': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3) + '@angular/compiler': 21.0.1 + '@angular/compiler-cli': 21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3) '@babel/core': 7.28.4 '@types/babel__core': 7.20.5 tinyglobby: 0.2.15 @@ -9298,62 +9677,61 @@ snapshots: transitivePeerDependencies: - supports-color - '@angular/material@21.0.0-next.10(a08c742fd8cc4091bdee765e9534f381)': + '@angular/material@21.0.0(8e7b236207d28338fccf527c5c162c22)': dependencies: - '@angular/cdk': 21.0.0-next.10(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/forms': 21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2) - '@angular/platform-browser': 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/cdk': 21.0.0(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/forms': 21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@standard-schema/spec@1.0.0)(rxjs@7.8.2) + '@angular/platform-browser': 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/b69a61793bd6ba935af262297688408d0b48252e(@modelcontextprotocol/sdk@1.20.1)': + '@angular/ng-dev@https://codeload.github.com/angular/dev-infra-private-ng-dev-builds/tar.gz/4c28145df03aff8c74d0a53f4f5602140e4d1a23(@modelcontextprotocol/sdk@1.20.1)': dependencies: '@actions/core': 1.11.1 '@google-cloud/spanner': 8.0.0(supports-color@10.2.2) - '@google/genai': 1.26.0(@modelcontextprotocol/sdk@1.20.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) - '@inquirer/prompts': 7.9.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) - '@octokit/auth-app': 8.1.1 - '@octokit/core': 7.0.5 - '@octokit/graphql': 9.0.2 - '@octokit/graphql-schema': 15.26.0 - '@octokit/openapi-types': 26.0.0 - '@octokit/plugin-paginate-rest': 13.2.1(@octokit/core@7.0.5) - '@octokit/plugin-rest-endpoint-methods': 16.1.1(@octokit/core@7.0.5) - '@octokit/request-error': 7.0.1 - '@octokit/rest': 22.0.0 - '@octokit/types': 15.0.1 - '@pnpm/dependency-path': 1001.1.3 + '@google/genai': 1.30.0(@modelcontextprotocol/sdk@1.20.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5) + '@inquirer/prompts': 8.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + '@octokit/auth-app': 8.1.2 + '@octokit/core': 7.0.6 + '@octokit/graphql': 9.0.3 + '@octokit/graphql-schema': 15.26.1 + '@octokit/openapi-types': 27.0.0 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) + '@octokit/request-error': 7.1.0 + '@octokit/rest': 22.0.1 + '@octokit/types': 16.0.0 + '@pnpm/dependency-path': 1001.1.5 '@types/cli-progress': 3.11.6 '@types/ejs': 3.1.5 '@types/events': 3.0.3 '@types/folder-hash': 4.0.4 - '@types/git-raw-commits': 5.0.0 - '@types/jasmine': 5.1.12 - '@types/node': 24.9.1 + '@types/git-raw-commits': 5.0.1 + '@types/jasmine': 5.1.13 + '@types/node': 24.10.1 '@types/semver': 7.7.1 '@types/which': 3.0.4 - '@types/yargs': 17.0.33 + '@types/yargs': 17.0.35 '@types/yarnpkg__lockfile': 1.1.9 '@yarnpkg/lockfile': 1.1.0 bufferutil: 4.0.9 - chalk: 5.6.2 cli-progress: 3.12.0 conventional-commits-filter: 5.0.0 conventional-commits-parser: 6.2.1 ejs: 3.1.10 encoding: 0.1.13 fast-glob: 3.3.3 - firebase: 12.4.0 + firebase: 12.6.0 folder-hash: 4.1.1(supports-color@10.2.2) git-raw-commits: 5.0.0(conventional-commits-filter@5.0.0)(conventional-commits-parser@6.2.1) jasmine: 5.12.0 - jasmine-core: 5.12.0 + jasmine-core: 5.12.1 jasmine-reporters: 2.5.2 jsonc-parser: 3.3.1 - minimatch: 10.0.3 + minimatch: 10.1.1 multimatch: 7.0.0 nock: 14.0.10 semver: 7.7.3 @@ -9362,46 +9740,46 @@ snapshots: typed-graphqlify: 3.1.6 typescript: 5.9.3 utf-8-validate: 6.0.5 - which: 5.0.0 + which: 6.0.0 yaml: 2.8.1 yargs: 18.0.0 transitivePeerDependencies: - '@modelcontextprotocol/sdk' - '@react-native-async-storage/async-storage' - '@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))': + '@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))': dependencies: - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) tslib: 2.8.1 optionalDependencies: - '@angular/animations': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/animations': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) - '@angular/platform-server@21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.0-next.9)(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/platform-server@21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/compiler@21.0.1)(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/compiler': 21.0.0-next.9 - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/compiler': 21.0.1 + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 xhr2: 0.2.1 - '@angular/router@21.0.0-next.9(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': + '@angular/router@21.0.1(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(@angular/platform-browser@21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(rxjs@7.8.2)': dependencies: - '@angular/common': 21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) - '@angular/platform-browser': 21.0.0-next.9(@angular/animations@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1)) + '@angular/common': 21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/platform-browser': 21.0.1(@angular/animations@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)))(@angular/common@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2))(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1)) rxjs: 7.8.2 tslib: 2.8.1 - '@angular/service-worker@21.0.0-next.9(@angular/core@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': + '@angular/service-worker@21.0.1(@angular/core@21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1))(rxjs@7.8.2)': dependencies: - '@angular/core': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(rxjs@7.8.2)(zone.js@0.15.1) + '@angular/core': 21.0.1(@angular/compiler@21.0.1)(rxjs@7.8.2)(zone.js@0.15.1) rxjs: 7.8.2 tslib: 2.8.1 - '@asamuzakjp/css-color@4.0.5': + '@asamuzakjp/css-color@4.1.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) @@ -9409,7 +9787,7 @@ snapshots: '@csstools/css-tokenizer': 3.0.4 lru-cache: 11.2.2 - '@asamuzakjp/dom-selector@6.7.2': + '@asamuzakjp/dom-selector@6.7.4': dependencies: '@asamuzakjp/nwsapi': 2.3.9 bidi-js: 1.0.3 @@ -9421,11 +9799,11 @@ snapshots: '@babel/code-frame@7.27.1': dependencies: - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.4': {} + '@babel/compat-data@7.28.5': {} '@babel/core@7.28.4': dependencies: @@ -9434,10 +9812,10 @@ snapshots: '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helpers': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3(supports-color@10.2.2) @@ -9449,38 +9827,46 @@ snapshots: '@babel/generator@7.28.3': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/generator@7.28.5': + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.26.3 + browserslist: 4.28.0 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)': + '@babel/helper-create-class-features-plugin@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)': + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-annotate-as-pure': 7.27.3 @@ -9500,17 +9886,17 @@ snapshots: '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.27.1': + '@babel/helper-member-expression-to-functions@7.28.5': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color @@ -9518,14 +9904,14 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-module-imports': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-plugin-utils@7.27.1': {} @@ -9534,58 +9920,58 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-member-expression-to-functions': 7.27.1 + '@babel/helper-member-expression-to-functions': 7.28.5 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helper-split-export-declaration@7.24.7': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@babel/helper-string-parser@7.27.1': {} - '@babel/helper-validator-identifier@7.27.1': {} + '@babel/helper-validator-identifier@7.28.5': {} '@babel/helper-validator-option@7.27.1': {} '@babel/helper-wrap-function@7.28.3': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.4 - '@babel/types': 7.28.4 + '@babel/traverse': 7.28.5 + '@babel/types': 7.28.5 transitivePeerDependencies: - supports-color '@babel/helpers@7.28.4': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/parser@7.28.4': + '@babel/parser@7.28.5': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9604,7 +9990,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.4) transitivePeerDependencies: - supports-color @@ -9612,7 +9998,7 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9633,7 +10019,7 @@ snapshots: '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)': @@ -9646,7 +10032,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9664,7 +10050,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)': + '@babel/plugin-transform-block-scoping@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 @@ -9672,7 +10058,7 @@ snapshots: '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -9680,7 +10066,7 @@ snapshots: '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -9693,7 +10079,7 @@ snapshots: '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9703,18 +10089,18 @@ snapshots: '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)': + '@babel/plugin-transform-destructuring@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)': @@ -9725,7 +10111,7 @@ snapshots: '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)': @@ -9737,11 +10123,11 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.4) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-exponentiation-operator@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 @@ -9764,7 +10150,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9778,7 +10164,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-logical-assignment-operators@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 @@ -9804,13 +10190,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-modules-systemjs@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.4 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9825,7 +10211,7 @@ snapshots: '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)': @@ -9848,9 +10234,9 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) - '@babel/traverse': 7.28.4 + '@babel/traverse': 7.28.5 transitivePeerDependencies: - supports-color @@ -9867,7 +10253,7 @@ snapshots: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)': + '@babel/plugin-transform-optional-chaining@7.28.5(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 @@ -9883,7 +10269,7 @@ snapshots: '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -9892,7 +10278,7 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4) + '@babel/helper-create-class-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color @@ -9910,7 +10296,7 @@ snapshots: '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)': @@ -9966,29 +10352,29 @@ snapshots: '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)': dependencies: '@babel/core': 7.28.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4) + '@babel/helper-create-regexp-features-plugin': 7.28.5(@babel/core@7.28.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/preset-env@7.28.3(@babel/core@7.28.4)': dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/core': 7.28.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.28.5(@babel/core@7.28.4) '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4) '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4) '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4) @@ -10001,28 +10387,28 @@ snapshots: '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4) '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4) + '@babel/plugin-transform-block-scoping': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4) '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4) '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4) + '@babel/plugin-transform-destructuring': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-exponentiation-operator': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-logical-assignment-operators': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-modules-systemjs': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4) @@ -10031,7 +10417,7 @@ snapshots: '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4) '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-transform-optional-chaining': 7.28.5(@babel/core@7.28.4) '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4) '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4) '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4) @@ -10052,7 +10438,7 @@ snapshots: babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4) babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4) babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 semver: 6.3.1 transitivePeerDependencies: - supports-color @@ -10061,7 +10447,7 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 esutils: 2.0.3 '@babel/runtime@7.28.4': {} @@ -10069,25 +10455,25 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 - '@babel/traverse@7.28.4': + '@babel/traverse@7.28.5': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.28.5 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - '@babel/types@7.28.4': + '@babel/types@7.28.5': dependencies: '@babel/helper-string-parser': 7.27.1 - '@babel/helper-validator-identifier': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 '@bazel/bazelisk@1.26.0': {} @@ -10127,127 +10513,281 @@ snapshots: dependencies: '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)': - dependencies: - postcss: 8.5.6 + '@csstools/css-syntax-patches-for-csstree@1.0.17': {} + + '@csstools/css-tokenizer@3.0.4': {} + + '@cypress/request@3.0.9': + dependencies: + aws-sign2: 0.7.0 + aws4: 1.13.2 + caseless: 0.12.0 + combined-stream: 1.0.8 + extend: 3.0.2 + forever-agent: 0.6.1 + form-data: 4.0.5 + http-signature: 1.4.0 + is-typedarray: 1.0.0 + isstream: 0.1.2 + json-stringify-safe: 5.0.1 + mime-types: 2.1.35 + performance-now: 2.1.0 + qs: 6.14.0 + safe-buffer: 5.2.1 + tough-cookie: 5.1.2 + tunnel-agent: 0.6.0 + uuid: 8.3.2 + + '@discoveryjs/json-ext@0.6.3': {} + + '@emnapi/core@1.7.1': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.7.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/aix-ppc64@0.26.0': + optional: true + + '@esbuild/aix-ppc64@0.27.0': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.26.0': + optional: true + + '@esbuild/android-arm64@0.27.0': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-arm@0.26.0': + optional: true + + '@esbuild/android-arm@0.27.0': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/android-x64@0.26.0': + optional: true + + '@esbuild/android-x64@0.27.0': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.26.0': + optional: true + + '@esbuild/darwin-arm64@0.27.0': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.26.0': + optional: true + + '@esbuild/darwin-x64@0.27.0': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.26.0': + optional: true + + '@esbuild/freebsd-arm64@0.27.0': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.26.0': + optional: true + + '@esbuild/freebsd-x64@0.27.0': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.26.0': + optional: true + + '@esbuild/linux-arm64@0.27.0': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-arm@0.26.0': + optional: true + + '@esbuild/linux-arm@0.27.0': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.26.0': + optional: true + + '@esbuild/linux-ia32@0.27.0': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.26.0': + optional: true + + '@esbuild/linux-loong64@0.27.0': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.26.0': + optional: true + + '@esbuild/linux-mips64el@0.27.0': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.26.0': + optional: true + + '@esbuild/linux-ppc64@0.27.0': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.26.0': + optional: true + + '@esbuild/linux-riscv64@0.27.0': + optional: true - '@csstools/css-tokenizer@3.0.4': {} + '@esbuild/linux-s390x@0.25.12': + optional: true - '@cypress/request@3.0.9': - dependencies: - aws-sign2: 0.7.0 - aws4: 1.13.2 - caseless: 0.12.0 - combined-stream: 1.0.8 - extend: 3.0.2 - forever-agent: 0.6.1 - form-data: 4.0.4 - http-signature: 1.4.0 - is-typedarray: 1.0.0 - isstream: 0.1.2 - json-stringify-safe: 5.0.1 - mime-types: 2.1.35 - performance-now: 2.1.0 - qs: 6.14.0 - safe-buffer: 5.2.1 - tough-cookie: 5.1.2 - tunnel-agent: 0.6.0 - uuid: 8.3.2 + '@esbuild/linux-s390x@0.26.0': + optional: true - '@discoveryjs/json-ext@0.6.3': {} + '@esbuild/linux-s390x@0.27.0': + optional: true - '@emnapi/core@1.5.0': - dependencies: - '@emnapi/wasi-threads': 1.1.0 - tslib: 2.8.1 + '@esbuild/linux-x64@0.25.12': optional: true - '@emnapi/runtime@1.5.0': - dependencies: - tslib: 2.8.1 + '@esbuild/linux-x64@0.26.0': optional: true - '@emnapi/wasi-threads@1.1.0': - dependencies: - tslib: 2.8.1 + '@esbuild/linux-x64@0.27.0': optional: true - '@esbuild/aix-ppc64@0.25.11': + '@esbuild/netbsd-arm64@0.25.12': optional: true - '@esbuild/android-arm64@0.25.11': + '@esbuild/netbsd-arm64@0.26.0': optional: true - '@esbuild/android-arm@0.25.11': + '@esbuild/netbsd-arm64@0.27.0': optional: true - '@esbuild/android-x64@0.25.11': + '@esbuild/netbsd-x64@0.25.12': optional: true - '@esbuild/darwin-arm64@0.25.11': + '@esbuild/netbsd-x64@0.26.0': optional: true - '@esbuild/darwin-x64@0.25.11': + '@esbuild/netbsd-x64@0.27.0': optional: true - '@esbuild/freebsd-arm64@0.25.11': + '@esbuild/openbsd-arm64@0.25.12': optional: true - '@esbuild/freebsd-x64@0.25.11': + '@esbuild/openbsd-arm64@0.26.0': optional: true - '@esbuild/linux-arm64@0.25.11': + '@esbuild/openbsd-arm64@0.27.0': optional: true - '@esbuild/linux-arm@0.25.11': + '@esbuild/openbsd-x64@0.25.12': optional: true - '@esbuild/linux-ia32@0.25.11': + '@esbuild/openbsd-x64@0.26.0': optional: true - '@esbuild/linux-loong64@0.25.11': + '@esbuild/openbsd-x64@0.27.0': optional: true - '@esbuild/linux-mips64el@0.25.11': + '@esbuild/openharmony-arm64@0.25.12': optional: true - '@esbuild/linux-ppc64@0.25.11': + '@esbuild/openharmony-arm64@0.26.0': optional: true - '@esbuild/linux-riscv64@0.25.11': + '@esbuild/openharmony-arm64@0.27.0': optional: true - '@esbuild/linux-s390x@0.25.11': + '@esbuild/sunos-x64@0.25.12': optional: true - '@esbuild/linux-x64@0.25.11': + '@esbuild/sunos-x64@0.26.0': optional: true - '@esbuild/netbsd-arm64@0.25.11': + '@esbuild/sunos-x64@0.27.0': optional: true - '@esbuild/netbsd-x64@0.25.11': + '@esbuild/win32-arm64@0.25.12': optional: true - '@esbuild/openbsd-arm64@0.25.11': + '@esbuild/win32-arm64@0.26.0': optional: true - '@esbuild/openbsd-x64@0.25.11': + '@esbuild/win32-arm64@0.27.0': optional: true - '@esbuild/openharmony-arm64@0.25.11': + '@esbuild/win32-ia32@0.25.12': optional: true - '@esbuild/sunos-x64@0.25.11': + '@esbuild/win32-ia32@0.26.0': optional: true - '@esbuild/win32-arm64@0.25.11': + '@esbuild/win32-ia32@0.27.0': optional: true - '@esbuild/win32-ia32@0.25.11': + '@esbuild/win32-x64@0.25.12': optional: true - '@esbuild/win32-x64@0.25.11': + '@esbuild/win32-x64@0.26.0': + optional: true + + '@esbuild/win32-x64@0.27.0': optional: true '@eslint-community/eslint-utils@4.9.0(eslint@9.38.0(jiti@2.6.1))': @@ -10255,7 +10795,7 @@ snapshots: eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 3.4.3 - '@eslint-community/regexpp@4.12.1': {} + '@eslint-community/regexpp@4.12.2': {} '@eslint/compat@1.4.0(eslint@9.38.0(jiti@2.6.1))': dependencies: @@ -10271,14 +10811,18 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.4.1': + '@eslint/config-helpers@0.4.2': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 '@eslint/core@0.16.0': dependencies: '@types/json-schema': 7.0.15 + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + '@eslint/eslintrc@3.3.1': dependencies: ajv: 6.12.6 @@ -10287,7 +10831,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -10297,16 +10841,16 @@ snapshots: '@eslint/object-schema@2.1.7': {} - '@eslint/plugin-kit@0.4.0': + '@eslint/plugin-kit@0.4.1': dependencies: - '@eslint/core': 0.16.0 + '@eslint/core': 0.17.0 levn: 0.4.1 '@fastify/busboy@2.1.1': {} - '@firebase/ai@2.4.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.4)': + '@firebase/ai@2.6.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/app-check-interop-types': 0.3.3 '@firebase/app-types': 0.9.3 '@firebase/component': 0.7.0 @@ -10314,11 +10858,11 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/analytics-compat@0.2.25(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/analytics-compat@0.2.25(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/analytics': 0.10.19(@firebase/app@0.14.4) + '@firebase/analytics': 0.10.19(@firebase/app@0.14.6) '@firebase/analytics-types': 0.8.3 - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10327,20 +10871,20 @@ snapshots: '@firebase/analytics-types@0.8.3': {} - '@firebase/analytics@0.10.19(@firebase/app@0.14.4)': + '@firebase/analytics@0.10.19(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/app-check-compat@0.4.0(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/app-check-compat@0.4.0(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-check': 0.11.0(@firebase/app@0.14.4) + '@firebase/app-check': 0.11.0(@firebase/app@0.14.6) '@firebase/app-check-types': 0.5.3 - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10352,17 +10896,17 @@ snapshots: '@firebase/app-check-types@0.5.3': {} - '@firebase/app-check@0.11.0(@firebase/app@0.14.4)': + '@firebase/app-check@0.11.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/app-compat@0.5.4': + '@firebase/app-compat@0.5.6': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10370,7 +10914,7 @@ snapshots: '@firebase/app-types@0.9.3': {} - '@firebase/app@0.14.4': + '@firebase/app@0.14.6': dependencies: '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 @@ -10378,10 +10922,10 @@ snapshots: idb: 7.1.1 tslib: 2.8.1 - '@firebase/auth-compat@0.6.0(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4)': + '@firebase/auth-compat@0.6.1(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 - '@firebase/auth': 1.11.0(@firebase/app@0.14.4) + '@firebase/app-compat': 0.5.6 + '@firebase/auth': 1.11.1(@firebase/app@0.14.6) '@firebase/auth-types': 0.13.0(@firebase/app-types@0.9.3)(@firebase/util@1.13.0) '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 @@ -10398,9 +10942,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/auth@1.11.0(@firebase/app@0.14.4)': + '@firebase/auth@1.11.1(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10411,9 +10955,9 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/data-connect@0.3.11(@firebase/app@0.14.4)': + '@firebase/data-connect@0.3.12(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/auth-interop-types': 0.2.4 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 @@ -10444,11 +10988,11 @@ snapshots: faye-websocket: 0.11.4 tslib: 2.8.1 - '@firebase/firestore-compat@0.4.2(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4)': + '@firebase/firestore-compat@0.4.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/firestore': 4.9.2(@firebase/app@0.14.4) + '@firebase/firestore': 4.9.2(@firebase/app@0.14.6) '@firebase/firestore-types': 3.0.3(@firebase/app-types@0.9.3)(@firebase/util@1.13.0) '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10461,9 +11005,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/firestore@4.9.2(@firebase/app@0.14.4)': + '@firebase/firestore@4.9.2(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 @@ -10472,11 +11016,11 @@ snapshots: '@grpc/proto-loader': 0.7.15 tslib: 2.8.1 - '@firebase/functions-compat@0.4.1(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/functions-compat@0.4.1(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/functions': 0.13.1(@firebase/app@0.14.4) + '@firebase/functions': 0.13.1(@firebase/app@0.14.6) '@firebase/functions-types': 0.6.3 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10485,9 +11029,9 @@ snapshots: '@firebase/functions-types@0.6.3': {} - '@firebase/functions@0.13.1(@firebase/app@0.14.4)': + '@firebase/functions@0.13.1(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/app-check-interop-types': 0.3.3 '@firebase/auth-interop-types': 0.2.4 '@firebase/component': 0.7.0 @@ -10495,11 +11039,11 @@ snapshots: '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/installations-compat@0.2.19(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4)': + '@firebase/installations-compat@0.2.19(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) '@firebase/installations-types': 0.5.3(@firebase/app-types@0.9.3) '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10511,9 +11055,9 @@ snapshots: dependencies: '@firebase/app-types': 0.9.3 - '@firebase/installations@0.6.19(@firebase/app@0.14.4)': + '@firebase/installations@0.6.19(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 idb: 7.1.1 @@ -10523,11 +11067,11 @@ snapshots: dependencies: tslib: 2.8.1 - '@firebase/messaging-compat@0.2.23(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/messaging-compat@0.2.23(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/messaging': 0.12.23(@firebase/app@0.14.4) + '@firebase/messaging': 0.12.23(@firebase/app@0.14.6) '@firebase/util': 1.13.0 tslib: 2.8.1 transitivePeerDependencies: @@ -10535,22 +11079,22 @@ snapshots: '@firebase/messaging-interop-types@0.2.3': {} - '@firebase/messaging@0.12.23(@firebase/app@0.14.4)': + '@firebase/messaging@0.12.23(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) '@firebase/messaging-interop-types': 0.2.3 '@firebase/util': 1.13.0 idb: 7.1.1 tslib: 2.8.1 - '@firebase/performance-compat@0.2.22(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/performance-compat@0.2.22(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 - '@firebase/performance': 0.7.9(@firebase/app@0.14.4) + '@firebase/performance': 0.7.9(@firebase/app@0.14.6) '@firebase/performance-types': 0.2.3 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10559,22 +11103,22 @@ snapshots: '@firebase/performance-types@0.2.3': {} - '@firebase/performance@0.7.9(@firebase/app@0.14.4)': + '@firebase/performance@0.7.9(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 tslib: 2.8.1 web-vitals: 4.2.4 - '@firebase/remote-config-compat@0.2.20(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4)': + '@firebase/remote-config-compat@0.2.20(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 '@firebase/logger': 0.5.0 - '@firebase/remote-config': 0.7.0(@firebase/app@0.14.4) + '@firebase/remote-config': 0.7.0(@firebase/app@0.14.6) '@firebase/remote-config-types': 0.5.0 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10583,20 +11127,20 @@ snapshots: '@firebase/remote-config-types@0.5.0': {} - '@firebase/remote-config@0.7.0(@firebase/app@0.14.4)': + '@firebase/remote-config@0.7.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) '@firebase/logger': 0.5.0 '@firebase/util': 1.13.0 tslib: 2.8.1 - '@firebase/storage-compat@0.4.0(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4)': + '@firebase/storage-compat@0.4.0(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6)': dependencies: - '@firebase/app-compat': 0.5.4 + '@firebase/app-compat': 0.5.6 '@firebase/component': 0.7.0 - '@firebase/storage': 0.14.0(@firebase/app@0.14.4) + '@firebase/storage': 0.14.0(@firebase/app@0.14.6) '@firebase/storage-types': 0.8.3(@firebase/app-types@0.9.3)(@firebase/util@1.13.0) '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10609,9 +11153,9 @@ snapshots: '@firebase/app-types': 0.9.3 '@firebase/util': 1.13.0 - '@firebase/storage@0.14.0(@firebase/app@0.14.4)': + '@firebase/storage@0.14.0(@firebase/app@0.14.6)': dependencies: - '@firebase/app': 0.14.4 + '@firebase/app': 0.14.6 '@firebase/component': 0.7.0 '@firebase/util': 1.13.0 tslib: 2.8.1 @@ -10631,7 +11175,7 @@ snapshots: arrify: 2.0.1 duplexify: 4.1.3 extend: 3.0.2 - google-auth-library: 10.4.1(supports-color@10.2.2) + google-auth-library: 10.5.0(supports-color@10.2.2) html-entities: 2.6.0 retry-request: 8.0.2(supports-color@10.2.2) teeny-request: 10.1.0(supports-color@10.2.2) @@ -10656,9 +11200,9 @@ snapshots: '@google-cloud/promisify': 5.0.0 '@grpc/proto-loader': 0.7.15 '@opentelemetry/api': 1.9.0 - '@opentelemetry/context-async-hooks': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/core': 2.1.0(@opentelemetry/api@1.9.0) - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/context-async-hooks': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 2.2.0(@opentelemetry/api@1.9.0) + '@opentelemetry/semantic-conventions': 1.38.0 '@types/big.js': 6.2.2 '@types/stack-trace': 0.0.33 big.js: 7.0.1 @@ -10666,8 +11210,8 @@ snapshots: duplexify: 4.1.3 events-intercept: 2.0.0 extend: 3.0.2 - google-auth-library: 10.4.1(supports-color@10.2.2) - google-gax: 5.0.4(supports-color@10.2.2) + google-auth-library: 10.5.0(supports-color@10.2.2) + google-gax: 5.0.6(supports-color@10.2.2) grpc-gcp: 1.0.1(protobufjs@7.5.4) is: 3.3.2 lodash.snakecase: 4.1.1 @@ -10683,9 +11227,9 @@ snapshots: transitivePeerDependencies: - supports-color - '@google/genai@1.26.0(@modelcontextprotocol/sdk@1.20.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': + '@google/genai@1.30.0(@modelcontextprotocol/sdk@1.20.1)(bufferutil@4.0.9)(supports-color@10.2.2)(utf-8-validate@6.0.5)': dependencies: - google-auth-library: 10.4.1(supports-color@10.2.2) + google-auth-library: 10.5.0(supports-color@10.2.2) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: '@modelcontextprotocol/sdk': 1.20.1 @@ -10694,7 +11238,7 @@ snapshots: - supports-color - utf-8-validate - '@grpc/grpc-js@1.14.0': + '@grpc/grpc-js@1.14.1': dependencies: '@grpc/proto-loader': 0.8.0 '@js-sdsl/ordered-map': 4.4.2 @@ -10702,7 +11246,7 @@ snapshots: '@grpc/grpc-js@1.9.15': dependencies: '@grpc/proto-loader': 0.7.15 - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@grpc/proto-loader@0.7.15': dependencies: @@ -10731,130 +11275,249 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@1.0.1': {} + '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.0(@types/node@24.9.1)': + '@inquirer/ansi@2.0.1': {} + + '@inquirer/checkbox@4.3.2(@types/node@24.10.1)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/checkbox@5.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/ansi': 2.0.1 + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/figures': 2.0.1 + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/confirm@5.1.19(@types/node@24.9.1)': + '@inquirer/confirm@5.1.19(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 - '@inquirer/core@10.3.0(@types/node@24.9.1)': + '@inquirer/confirm@6.0.1(@types/node@24.10.1)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/core@10.3.2(@types/node@24.10.1)': + dependencies: + '@inquirer/ansi': 1.0.2 + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/core@11.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/ansi': 2.0.1 + '@inquirer/figures': 2.0.1 + '@inquirer/type': 4.0.1(@types/node@24.10.1) + cli-width: 4.1.0 + mute-stream: 3.0.0 + signal-exit: 4.1.0 + wrap-ansi: 9.0.2 + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/editor@4.2.23(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/external-editor': 1.0.3(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/editor@4.2.21(@types/node@24.9.1)': + '@inquirer/editor@5.0.1(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/external-editor': 1.0.2(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/external-editor': 2.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 - '@inquirer/expand@4.0.21(@types/node@24.9.1)': + '@inquirer/expand@4.0.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/expand@5.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/external-editor@1.0.3(@types/node@24.10.1)': + dependencies: + chardet: 2.1.1 + iconv-lite: 0.7.0 + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/external-editor@1.0.2(@types/node@24.9.1)': + '@inquirer/external-editor@2.0.1(@types/node@24.10.1)': dependencies: - chardet: 2.1.0 + chardet: 2.1.1 iconv-lite: 0.7.0 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/figures@1.0.15': {} + + '@inquirer/figures@2.0.1': {} + + '@inquirer/input@4.3.1(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/figures@1.0.14': {} + '@inquirer/input@5.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/number@3.0.23(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/input@4.2.5(@types/node@24.9.1)': + '@inquirer/number@4.0.1(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 - '@inquirer/number@3.0.21(@types/node@24.9.1)': + '@inquirer/password@4.0.23(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 - '@inquirer/password@4.0.21(@types/node@24.9.1)': + '@inquirer/password@5.0.1(@types/node@24.10.1)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/ansi': 2.0.1 + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/prompts@7.9.0(@types/node@24.10.1)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.10.1) + '@inquirer/confirm': 5.1.19(@types/node@24.10.1) + '@inquirer/editor': 4.2.23(@types/node@24.10.1) + '@inquirer/expand': 4.0.23(@types/node@24.10.1) + '@inquirer/input': 4.3.1(@types/node@24.10.1) + '@inquirer/number': 3.0.23(@types/node@24.10.1) + '@inquirer/password': 4.0.23(@types/node@24.10.1) + '@inquirer/rawlist': 4.1.11(@types/node@24.10.1) + '@inquirer/search': 3.2.2(@types/node@24.10.1) + '@inquirer/select': 4.4.2(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 - - '@inquirer/prompts@7.9.0(@types/node@24.9.1)': - dependencies: - '@inquirer/checkbox': 4.3.0(@types/node@24.9.1) - '@inquirer/confirm': 5.1.19(@types/node@24.9.1) - '@inquirer/editor': 4.2.21(@types/node@24.9.1) - '@inquirer/expand': 4.0.21(@types/node@24.9.1) - '@inquirer/input': 4.2.5(@types/node@24.9.1) - '@inquirer/number': 3.0.21(@types/node@24.9.1) - '@inquirer/password': 4.0.21(@types/node@24.9.1) - '@inquirer/rawlist': 4.1.9(@types/node@24.9.1) - '@inquirer/search': 3.2.0(@types/node@24.9.1) - '@inquirer/select': 4.4.0(@types/node@24.9.1) + '@types/node': 24.10.1 + + '@inquirer/prompts@8.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/checkbox': 5.0.1(@types/node@24.10.1) + '@inquirer/confirm': 6.0.1(@types/node@24.10.1) + '@inquirer/editor': 5.0.1(@types/node@24.10.1) + '@inquirer/expand': 5.0.1(@types/node@24.10.1) + '@inquirer/input': 5.0.1(@types/node@24.10.1) + '@inquirer/number': 4.0.1(@types/node@24.10.1) + '@inquirer/password': 5.0.1(@types/node@24.10.1) + '@inquirer/rawlist': 5.0.1(@types/node@24.10.1) + '@inquirer/search': 4.0.1(@types/node@24.10.1) + '@inquirer/select': 5.0.1(@types/node@24.10.1) optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 - '@inquirer/rawlist@4.1.9(@types/node@24.9.1)': + '@inquirer/rawlist@4.1.11(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/rawlist@5.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/search@3.2.0(@types/node@24.9.1)': + '@inquirer/search@3.2.2(@types/node@24.10.1)': dependencies: - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/search@4.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/figures': 2.0.1 + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/select@4.4.0(@types/node@24.9.1)': + '@inquirer/select@4.4.2(@types/node@24.10.1)': dependencies: - '@inquirer/ansi': 1.0.1 - '@inquirer/core': 10.3.0(@types/node@24.9.1) - '@inquirer/figures': 1.0.14 - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/ansi': 1.0.2 + '@inquirer/core': 10.3.2(@types/node@24.10.1) + '@inquirer/figures': 1.0.15 + '@inquirer/type': 3.0.10(@types/node@24.10.1) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 + + '@inquirer/select@5.0.1(@types/node@24.10.1)': + dependencies: + '@inquirer/ansi': 2.0.1 + '@inquirer/core': 11.0.1(@types/node@24.10.1) + '@inquirer/figures': 2.0.1 + '@inquirer/type': 4.0.1(@types/node@24.10.1) + optionalDependencies: + '@types/node': 24.10.1 + + '@inquirer/type@3.0.10(@types/node@24.10.1)': + optionalDependencies: + '@types/node': 24.10.1 - '@inquirer/type@3.0.9(@types/node@24.9.1)': + '@inquirer/type@4.0.1(@types/node@24.10.1)': optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 '@isaacs/balanced-match@4.0.1': {} @@ -10946,10 +11609,10 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} - '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.9.0(@types/node@24.9.1))(@types/node@24.9.1)(listr2@9.0.5)': + '@listr2/prompt-adapter-inquirer@3.0.5(@inquirer/prompts@7.9.0(@types/node@24.10.1))(@types/node@24.10.1)(listr2@9.0.5)': dependencies: - '@inquirer/prompts': 7.9.0(@types/node@24.9.1) - '@inquirer/type': 3.0.9(@types/node@24.9.1) + '@inquirer/prompts': 7.9.0(@types/node@24.10.1) + '@inquirer/type': 3.0.10(@types/node@24.10.1) listr2: 9.0.5 transitivePeerDependencies: - '@types/node' @@ -10985,10 +11648,10 @@ snapshots: eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) - pkce-challenge: 5.0.0 - raw-body: 3.0.1 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod-to-json-schema: 3.25.0(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -11093,8 +11756,8 @@ snapshots: '@napi-rs/wasm-runtime@1.0.7': dependencies: - '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 + '@emnapi/core': 1.7.1 + '@emnapi/runtime': 1.7.1 '@tybys/wasm-util': 0.10.1 optional: true @@ -11110,16 +11773,6 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@npmcli/agent@3.0.0': - dependencies: - agent-base: 7.1.4 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6(supports-color@10.2.2) - lru-cache: 10.4.3 - socks-proxy-agent: 8.0.5 - transitivePeerDependencies: - - supports-color - '@npmcli/agent@4.0.0': dependencies: agent-base: 7.1.4 @@ -11130,35 +11783,35 @@ snapshots: transitivePeerDependencies: - supports-color - '@npmcli/fs@4.0.0': + '@npmcli/fs@5.0.0': dependencies: semver: 7.7.3 - '@npmcli/git@7.0.0': + '@npmcli/git@7.0.1': dependencies: - '@npmcli/promise-spawn': 8.0.3 - ini: 5.0.0 + '@npmcli/promise-spawn': 9.0.1 + ini: 6.0.0 lru-cache: 11.2.2 - npm-pick-manifest: 11.0.1 - proc-log: 5.0.0 + npm-pick-manifest: 11.0.3 + proc-log: 6.0.0 promise-retry: 2.0.1 semver: 7.7.3 - which: 5.0.0 + which: 6.0.0 '@npmcli/installed-package-contents@3.0.0': dependencies: npm-bundled: 4.0.0 npm-normalize-package-bin: 4.0.0 - '@npmcli/node-gyp@4.0.0': {} + '@npmcli/node-gyp@5.0.0': {} - '@npmcli/package-json@7.0.1': + '@npmcli/package-json@7.0.4': dependencies: - '@npmcli/git': 7.0.0 - glob: 11.0.3 + '@npmcli/git': 7.0.1 + glob: 13.0.0 hosted-git-info: 9.0.2 - json-parse-even-better-errors: 4.0.0 - proc-log: 5.0.0 + json-parse-even-better-errors: 5.0.0 + proc-log: 6.0.0 semver: 7.7.3 validate-npm-package-license: 3.0.4 @@ -11166,128 +11819,132 @@ snapshots: dependencies: which: 5.0.0 - '@npmcli/redact@3.2.2': {} + '@npmcli/promise-spawn@9.0.1': + dependencies: + which: 6.0.0 + + '@npmcli/redact@4.0.0': {} - '@npmcli/run-script@10.0.0': + '@npmcli/run-script@10.0.3': dependencies: - '@npmcli/node-gyp': 4.0.0 - '@npmcli/package-json': 7.0.1 - '@npmcli/promise-spawn': 8.0.3 - node-gyp: 11.5.0 - proc-log: 5.0.0 - which: 5.0.0 + '@npmcli/node-gyp': 5.0.0 + '@npmcli/package-json': 7.0.4 + '@npmcli/promise-spawn': 9.0.1 + node-gyp: 12.1.0 + proc-log: 6.0.0 + which: 6.0.0 transitivePeerDependencies: - supports-color - '@octokit/auth-app@8.1.1': + '@octokit/auth-app@8.1.2': dependencies: - '@octokit/auth-oauth-app': 9.0.2 - '@octokit/auth-oauth-user': 6.0.1 - '@octokit/request': 10.0.5 - '@octokit/request-error': 7.0.1 - '@octokit/types': 15.0.1 + '@octokit/auth-oauth-app': 9.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.7 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 toad-cache: 3.7.0 universal-github-app-jwt: 2.2.2 universal-user-agent: 7.0.3 - '@octokit/auth-oauth-app@9.0.2': + '@octokit/auth-oauth-app@9.0.3': dependencies: - '@octokit/auth-oauth-device': 8.0.2 - '@octokit/auth-oauth-user': 6.0.1 - '@octokit/request': 10.0.5 - '@octokit/types': 15.0.1 + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/auth-oauth-user': 6.0.2 + '@octokit/request': 10.0.7 + '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 - '@octokit/auth-oauth-device@8.0.2': + '@octokit/auth-oauth-device@8.0.3': dependencies: - '@octokit/oauth-methods': 6.0.1 - '@octokit/request': 10.0.5 - '@octokit/types': 15.0.1 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.7 + '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 - '@octokit/auth-oauth-user@6.0.1': + '@octokit/auth-oauth-user@6.0.2': dependencies: - '@octokit/auth-oauth-device': 8.0.2 - '@octokit/oauth-methods': 6.0.1 - '@octokit/request': 10.0.5 - '@octokit/types': 15.0.1 + '@octokit/auth-oauth-device': 8.0.3 + '@octokit/oauth-methods': 6.0.2 + '@octokit/request': 10.0.7 + '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 '@octokit/auth-token@6.0.0': {} - '@octokit/core@7.0.5': + '@octokit/core@7.0.6': dependencies: '@octokit/auth-token': 6.0.0 - '@octokit/graphql': 9.0.2 - '@octokit/request': 10.0.5 - '@octokit/request-error': 7.0.1 - '@octokit/types': 15.0.1 + '@octokit/graphql': 9.0.3 + '@octokit/request': 10.0.7 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 before-after-hook: 4.0.0 universal-user-agent: 7.0.3 - '@octokit/endpoint@11.0.1': + '@octokit/endpoint@11.0.2': dependencies: - '@octokit/types': 15.0.1 + '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 - '@octokit/graphql-schema@15.26.0': + '@octokit/graphql-schema@15.26.1': dependencies: - graphql: 16.11.0 - graphql-tag: 2.12.6(graphql@16.11.0) + graphql: 16.12.0 + graphql-tag: 2.12.6(graphql@16.12.0) - '@octokit/graphql@9.0.2': + '@octokit/graphql@9.0.3': dependencies: - '@octokit/request': 10.0.5 - '@octokit/types': 15.0.1 + '@octokit/request': 10.0.7 + '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 '@octokit/oauth-authorization-url@8.0.0': {} - '@octokit/oauth-methods@6.0.1': + '@octokit/oauth-methods@6.0.2': dependencies: '@octokit/oauth-authorization-url': 8.0.0 - '@octokit/request': 10.0.5 - '@octokit/request-error': 7.0.1 - '@octokit/types': 15.0.1 + '@octokit/request': 10.0.7 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 - '@octokit/openapi-types@26.0.0': {} + '@octokit/openapi-types@27.0.0': {} - '@octokit/plugin-paginate-rest@13.2.1(@octokit/core@7.0.5)': + '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': dependencies: - '@octokit/core': 7.0.5 - '@octokit/types': 15.0.1 + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 - '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.5)': + '@octokit/plugin-request-log@6.0.0(@octokit/core@7.0.6)': dependencies: - '@octokit/core': 7.0.5 + '@octokit/core': 7.0.6 - '@octokit/plugin-rest-endpoint-methods@16.1.1(@octokit/core@7.0.5)': + '@octokit/plugin-rest-endpoint-methods@17.0.0(@octokit/core@7.0.6)': dependencies: - '@octokit/core': 7.0.5 - '@octokit/types': 15.0.1 + '@octokit/core': 7.0.6 + '@octokit/types': 16.0.0 - '@octokit/request-error@7.0.1': + '@octokit/request-error@7.1.0': dependencies: - '@octokit/types': 15.0.1 + '@octokit/types': 16.0.0 - '@octokit/request@10.0.5': + '@octokit/request@10.0.7': dependencies: - '@octokit/endpoint': 11.0.1 - '@octokit/request-error': 7.0.1 - '@octokit/types': 15.0.1 + '@octokit/endpoint': 11.0.2 + '@octokit/request-error': 7.1.0 + '@octokit/types': 16.0.0 fast-content-type-parse: 3.0.0 universal-user-agent: 7.0.3 - '@octokit/rest@22.0.0': + '@octokit/rest@22.0.1': dependencies: - '@octokit/core': 7.0.5 - '@octokit/plugin-paginate-rest': 13.2.1(@octokit/core@7.0.5) - '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.5) - '@octokit/plugin-rest-endpoint-methods': 16.1.1(@octokit/core@7.0.5) + '@octokit/core': 7.0.6 + '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) + '@octokit/plugin-request-log': 6.0.0(@octokit/core@7.0.6) + '@octokit/plugin-rest-endpoint-methods': 17.0.0(@octokit/core@7.0.6) - '@octokit/types@15.0.1': + '@octokit/types@16.0.0': dependencies: - '@octokit/openapi-types': 26.0.0 + '@octokit/openapi-types': 27.0.0 '@open-draft/deferred-promise@2.2.0': {} @@ -11300,18 +11957,18 @@ snapshots: '@opentelemetry/api@1.9.0': {} - '@opentelemetry/context-async-hooks@2.1.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/context-async-hooks@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core@2.1.0(@opentelemetry/api@1.9.0)': + '@opentelemetry/core@2.2.0(@opentelemetry/api@1.9.0)': dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.37.0 + '@opentelemetry/semantic-conventions': 1.38.0 - '@opentelemetry/semantic-conventions@1.37.0': {} + '@opentelemetry/semantic-conventions@1.38.0': {} - '@oxc-project/types@0.95.0': {} + '@oxc-project/types@0.96.0': {} '@parcel/watcher-android-arm64@2.5.1': optional: true @@ -11385,17 +12042,17 @@ snapshots: '@pnpm/crypto.polyfill@1000.1.0': {} - '@pnpm/dependency-path@1001.1.3': + '@pnpm/dependency-path@1001.1.5': dependencies: '@pnpm/crypto.hash': 1000.2.1 - '@pnpm/types': 1000.9.0 + '@pnpm/types': 1001.0.1 semver: 7.7.3 '@pnpm/graceful-fs@1000.0.1': dependencies: graceful-fs: 4.2.11 - '@pnpm/types@1000.9.0': {} + '@pnpm/types@1001.0.1': {} '@protobufjs/aspromise@1.1.2': {} @@ -11420,7 +12077,7 @@ snapshots: '@protobufjs/utf8@1.1.0': {} - '@puppeteer/browsers@2.10.12': + '@puppeteer/browsers@2.10.13': dependencies: debug: 4.4.3(supports-color@10.2.2) extract-zip: 2.0.1 @@ -11435,57 +12092,57 @@ snapshots: - react-native-b4a - supports-color - '@rolldown/binding-android-arm64@1.0.0-beta.44': + '@rolldown/binding-android-arm64@1.0.0-beta.47': optional: true - '@rolldown/binding-darwin-arm64@1.0.0-beta.44': + '@rolldown/binding-darwin-arm64@1.0.0-beta.47': optional: true - '@rolldown/binding-darwin-x64@1.0.0-beta.44': + '@rolldown/binding-darwin-x64@1.0.0-beta.47': optional: true - '@rolldown/binding-freebsd-x64@1.0.0-beta.44': + '@rolldown/binding-freebsd-x64@1.0.0-beta.47': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.44': + '@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.47': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.44': + '@rolldown/binding-linux-arm64-gnu@1.0.0-beta.47': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.0-beta.44': + '@rolldown/binding-linux-arm64-musl@1.0.0-beta.47': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.0-beta.44': + '@rolldown/binding-linux-x64-gnu@1.0.0-beta.47': optional: true - '@rolldown/binding-linux-x64-musl@1.0.0-beta.44': + '@rolldown/binding-linux-x64-musl@1.0.0-beta.47': optional: true - '@rolldown/binding-openharmony-arm64@1.0.0-beta.44': + '@rolldown/binding-openharmony-arm64@1.0.0-beta.47': optional: true - '@rolldown/binding-wasm32-wasi@1.0.0-beta.44': + '@rolldown/binding-wasm32-wasi@1.0.0-beta.47': dependencies: '@napi-rs/wasm-runtime': 1.0.7 optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-arm64-msvc@1.0.0-beta.47': optional: true - '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-ia32-msvc@1.0.0-beta.47': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.0-beta.44': + '@rolldown/binding-win32-x64-msvc@1.0.0-beta.47': optional: true - '@rolldown/pluginutils@1.0.0-beta.44': {} + '@rolldown/pluginutils@1.0.0-beta.47': {} '@rollup/plugin-alias@5.1.1(rollup@4.52.5)': optionalDependencies: rollup: 4.52.5 - '@rollup/plugin-commonjs@28.0.8(rollup@4.52.5)': + '@rollup/plugin-commonjs@28.0.9(rollup@4.52.5)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.52.5) commondir: 1.0.1 @@ -11497,12 +12154,6 @@ snapshots: optionalDependencies: rollup: 4.52.5 - '@rollup/plugin-json@6.1.0(rollup@4.52.4)': - dependencies: - '@rollup/pluginutils': 5.3.0(rollup@4.52.4) - optionalDependencies: - rollup: 4.52.4 - '@rollup/plugin-json@6.1.0(rollup@4.52.5)': dependencies: '@rollup/pluginutils': 5.3.0(rollup@4.52.5) @@ -11537,14 +12188,6 @@ snapshots: optionalDependencies: rollup: 4.52.5 - '@rollup/pluginutils@5.3.0(rollup@4.52.4)': - dependencies: - '@types/estree': 1.0.8 - estree-walker: 2.0.2 - picomatch: 4.0.3 - optionalDependencies: - rollup: 4.52.4 - '@rollup/pluginutils@5.3.0(rollup@4.52.5)': dependencies: '@types/estree': 1.0.8 @@ -11553,139 +12196,73 @@ snapshots: optionalDependencies: rollup: 4.52.5 - '@rollup/rollup-android-arm-eabi@4.52.4': - optional: true - '@rollup/rollup-android-arm-eabi@4.52.5': optional: true - '@rollup/rollup-android-arm64@4.52.4': - optional: true - '@rollup/rollup-android-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-arm64@4.52.4': - optional: true - '@rollup/rollup-darwin-arm64@4.52.5': optional: true - '@rollup/rollup-darwin-x64@4.52.4': - optional: true - '@rollup/rollup-darwin-x64@4.52.5': optional: true - '@rollup/rollup-freebsd-arm64@4.52.4': - optional: true - '@rollup/rollup-freebsd-arm64@4.52.5': optional: true - '@rollup/rollup-freebsd-x64@4.52.4': - optional: true - '@rollup/rollup-freebsd-x64@4.52.5': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.4': - optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.4': - optional: true - '@rollup/rollup-linux-arm-musleabihf@4.52.5': optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-arm64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-arm64-musl@4.52.4': - optional: true - '@rollup/rollup-linux-arm64-musl@4.52.5': optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-loong64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-ppc64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-riscv64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.4': - optional: true - '@rollup/rollup-linux-riscv64-musl@4.52.5': optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-s390x-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-gnu@4.52.4': - optional: true - '@rollup/rollup-linux-x64-gnu@4.52.5': optional: true - '@rollup/rollup-linux-x64-musl@4.52.4': - optional: true - '@rollup/rollup-linux-x64-musl@4.52.5': optional: true - '@rollup/rollup-openharmony-arm64@4.52.4': - optional: true - '@rollup/rollup-openharmony-arm64@4.52.5': optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.4': - optional: true - '@rollup/rollup-win32-arm64-msvc@4.52.5': optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.4': - optional: true - '@rollup/rollup-win32-ia32-msvc@4.52.5': optional: true - '@rollup/rollup-win32-x64-gnu@4.52.4': - optional: true - '@rollup/rollup-win32-x64-gnu@4.52.5': optional: true - '@rollup/rollup-win32-x64-msvc@4.52.4': - optional: true - '@rollup/rollup-win32-x64-msvc@4.52.5': optional: true - '@rollup/wasm-node@4.52.5': + '@rollup/wasm-node@4.53.3': dependencies: '@types/estree': 1.0.8 optionalDependencies: @@ -11706,7 +12283,7 @@ snapshots: '@sigstore/bundle': 4.0.0 '@sigstore/core': 3.0.0 '@sigstore/protobuf-specs': 0.5.0 - make-fetch-happen: 15.0.2 + make-fetch-happen: 15.0.3 proc-log: 5.0.0 promise-retry: 2.0.1 transitivePeerDependencies: @@ -11731,10 +12308,10 @@ snapshots: '@standard-schema/spec@1.0.0': {} - '@stylistic/eslint-plugin@5.5.0(eslint@9.38.0(jiti@2.6.1))': + '@stylistic/eslint-plugin@5.6.1(eslint@9.38.0(jiti@2.6.1))': dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) - '@typescript-eslint/types': 8.46.1 + '@typescript-eslint/types': 8.48.0 eslint: 9.38.0(jiti@2.6.1) eslint-visitor-keys: 4.2.1 espree: 10.4.0 @@ -11749,7 +12326,7 @@ snapshots: '@tootallnate/quickjs-emscripten@0.23.0': {} - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -11771,60 +12348,61 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/babel__code-frame@7.0.6': {} '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.28.0 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.28.4 + '@babel/types': 7.28.5 '@types/big.js@6.2.2': {} '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/bonjour@3.5.13': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 - '@types/browser-sync@2.29.0': + '@types/browser-sync@2.29.1': dependencies: '@types/micromatch': 2.3.35 - '@types/node': 22.18.11 - '@types/serve-static': 1.15.9 + '@types/node': 22.19.1 + '@types/serve-static': 2.2.0 chokidar: 3.6.0 - '@types/chai@5.2.2': + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 '@types/cli-progress@3.11.6': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/co-body@6.1.3': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/qs': 6.14.0 '@types/command-line-args@5.2.3': {} @@ -11832,34 +12410,34 @@ snapshots: '@types/connect-history-api-fallback@1.5.4': dependencies: '@types/express-serve-static-core': 4.19.7 - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/connect@3.4.38': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/content-disposition@0.5.9': {} '@types/convert-source-map@2.0.3': {} - '@types/cookies@0.9.1': + '@types/cookies@0.9.2': dependencies: '@types/connect': 3.4.38 - '@types/express': 5.0.3 + '@types/express': 5.0.5 '@types/keygrip': 1.0.6 - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/cors@2.8.19': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/debounce@1.2.4': {} '@types/deep-eql@4.0.2': {} - '@types/duplexify@3.6.4': + '@types/duplexify@3.6.5': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/ejs@3.1.5': {} @@ -11879,48 +12457,48 @@ snapshots: '@types/express-serve-static-core@4.19.7': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 - '@types/send': 1.2.0 + '@types/send': 1.2.1 '@types/express-serve-static-core@5.1.0': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 - '@types/send': 1.2.0 + '@types/send': 1.2.1 - '@types/express@4.17.23': + '@types/express@4.17.25': dependencies: '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 4.19.7 '@types/qs': 6.14.0 - '@types/serve-static': 1.15.9 + '@types/serve-static': 1.15.10 - '@types/express@5.0.3': + '@types/express@5.0.5': dependencies: '@types/body-parser': 1.19.6 '@types/express-serve-static-core': 5.1.0 - '@types/serve-static': 1.15.9 + '@types/serve-static': 1.15.10 '@types/folder-hash@4.0.4': {} - '@types/git-raw-commits@5.0.0': + '@types/git-raw-commits@5.0.1': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/http-assert@1.5.6': {} '@types/http-errors@2.0.5': {} - '@types/http-proxy@1.17.16': + '@types/http-proxy@1.17.17': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/ini@4.1.1': {} @@ -11936,9 +12514,9 @@ snapshots: '@types/jasmine-reporters@2.5.3': dependencies: - '@types/jasmine': 5.1.12 + '@types/jasmine': 5.1.13 - '@types/jasmine@5.1.12': {} + '@types/jasmine@5.1.13': {} '@types/json-schema@7.0.15': {} @@ -11946,14 +12524,14 @@ snapshots: '@types/karma@6.3.9': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 log4js: 6.9.1 transitivePeerDependencies: - supports-color '@types/keygrip@1.0.6': {} - '@types/koa-compose@3.2.8': + '@types/koa-compose@3.2.9': dependencies: '@types/koa': 2.15.0 @@ -11961,26 +12539,26 @@ snapshots: dependencies: '@types/accepts': 1.3.7 '@types/content-disposition': 0.5.9 - '@types/cookies': 0.9.1 + '@types/cookies': 0.9.2 '@types/http-assert': 1.5.6 '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 - '@types/koa-compose': 3.2.8 - '@types/node': 22.18.11 + '@types/koa-compose': 3.2.9 + '@types/node': 22.19.1 '@types/less@3.0.8': {} - '@types/loader-utils@3.0.0(esbuild@0.25.11)': + '@types/loader-utils@3.0.0(esbuild@0.26.0)': dependencies: - '@types/node': 22.18.11 - webpack: 5.102.1(esbuild@0.25.11) + '@types/node': 22.19.1 + webpack: 5.102.1(esbuild@0.26.0) transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - webpack-cli - '@types/lodash@4.17.20': {} + '@types/lodash@4.17.21': {} '@types/micromatch@2.3.35': dependencies: @@ -11990,26 +12568,26 @@ snapshots: '@types/node-fetch@2.6.13': dependencies: - '@types/node': 22.18.11 - form-data: 4.0.4 + '@types/node': 22.19.1 + form-data: 4.0.5 '@types/node-forge@1.3.14': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 - '@types/node@22.18.11': + '@types/node@22.19.1': dependencies: - undici-types: 6.21.0 + undici-types: 7.16.0 - '@types/node@24.9.1': + '@types/node@24.10.1': dependencies: undici-types: 7.16.0 '@types/npm-package-arg@6.1.4': {} - '@types/npm-registry-fetch@8.0.8': + '@types/npm-registry-fetch@8.0.9': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/node-fetch': 2.6.13 '@types/npm-package-arg': 6.1.4 '@types/npmlog': 7.0.0 @@ -12017,12 +12595,12 @@ snapshots: '@types/npmlog@7.0.0': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/pacote@11.1.8': dependencies: - '@types/node': 22.18.11 - '@types/npm-registry-fetch': 8.0.8 + '@types/node': 22.19.1 + '@types/npm-registry-fetch': 8.0.9 '@types/npmlog': 7.0.0 '@types/ssri': 7.1.5 @@ -12034,12 +12612,12 @@ snapshots: '@types/progress@2.0.7': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 - '@types/pumpify@1.4.4': + '@types/pumpify@1.4.5': dependencies: - '@types/duplexify': 3.6.4 - '@types/node': 22.18.11 + '@types/duplexify': 3.6.5 + '@types/node': 22.19.1 '@types/q@0.0.32': {} @@ -12053,7 +12631,7 @@ snapshots: '@types/responselike@1.0.0': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/retry@0.12.2': {} @@ -12061,53 +12639,58 @@ snapshots: '@types/semver@7.7.1': {} - '@types/send@0.17.5': + '@types/send@0.17.6': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.18.11 + '@types/node': 22.19.1 - '@types/send@1.2.0': + '@types/send@1.2.1': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/serve-index@1.9.4': dependencies: - '@types/express': 5.0.3 + '@types/express': 5.0.5 + + '@types/serve-static@1.15.10': + dependencies: + '@types/http-errors': 2.0.5 + '@types/node': 22.19.1 + '@types/send': 0.17.6 - '@types/serve-static@1.15.9': + '@types/serve-static@2.2.0': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.18.11 - '@types/send': 0.17.5 + '@types/node': 22.19.1 '@types/sockjs@0.3.36': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/ssri@7.1.5': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/stack-trace@0.0.33': {} - '@types/watchpack@2.4.4': + '@types/watchpack@2.4.5': dependencies: '@types/graceful-fs': 4.1.9 - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/which@3.0.4': {} '@types/ws@7.4.7': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/ws@8.18.1': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.33': + '@types/yargs@17.0.35': dependencies: '@types/yargs-parser': 21.0.3 @@ -12115,12 +12698,12 @@ snapshots: '@types/yauzl@2.10.3': dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 optional: true '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': dependencies: - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/regexpp': 4.12.2 '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) '@typescript-eslint/scope-manager': 8.46.2 '@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) @@ -12177,10 +12760,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.46.1': {} - '@typescript-eslint/types@8.46.2': {} + '@typescript-eslint/types@8.48.0': {} + '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': dependencies: '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) @@ -12373,64 +12956,64 @@ snapshots: lodash: 4.17.21 minimatch: 7.4.6 - '@vitejs/plugin-basic-ssl@2.1.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + '@vitejs/plugin-basic-ssl@2.1.0(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/coverage-v8@4.0.0(vitest@4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/coverage-v8@4.0.8(vitest@4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.0.0 - ast-v8-to-istanbul: 0.3.7 + '@vitest/utils': 4.0.8 + ast-v8-to-istanbul: 0.3.8 debug: 4.4.3(supports-color@10.2.2) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.6 istanbul-reports: 3.2.0 - magicast: 0.3.5 + magicast: 0.5.1 std-env: 3.10.0 tinyrainbow: 3.0.3 - vitest: 4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vitest: 4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) transitivePeerDependencies: - supports-color - '@vitest/expect@4.0.0': + '@vitest/expect@4.0.8': dependencies: '@standard-schema/spec': 1.0.0 - '@types/chai': 5.2.2 - '@vitest/spy': 4.0.0 - '@vitest/utils': 4.0.0 - chai: 6.2.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.8 + '@vitest/utils': 4.0.8 + chai: 6.2.1 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': + '@vitest/mocker@4.0.8(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1))': dependencies: - '@vitest/spy': 4.0.0 + '@vitest/spy': 4.0.8 estree-walker: 3.0.3 - magic-string: 0.30.19 + magic-string: 0.30.21 optionalDependencies: - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) - '@vitest/pretty-format@4.0.0': + '@vitest/pretty-format@4.0.8': dependencies: tinyrainbow: 3.0.3 - '@vitest/runner@4.0.0': + '@vitest/runner@4.0.8': dependencies: - '@vitest/utils': 4.0.0 + '@vitest/utils': 4.0.8 pathe: 2.0.3 - '@vitest/snapshot@4.0.0': + '@vitest/snapshot@4.0.8': dependencies: - '@vitest/pretty-format': 4.0.0 - magic-string: 0.30.19 + '@vitest/pretty-format': 4.0.8 + magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.0': {} + '@vitest/spy@4.0.8': {} - '@vitest/utils@4.0.0': + '@vitest/utils@4.0.8': dependencies: - '@vitest/pretty-format': 4.0.0 + '@vitest/pretty-format': 4.0.8 tinyrainbow: 3.0.3 '@web/browser-logs@0.4.1': @@ -12449,7 +13032,7 @@ snapshots: es-module-lexer: 1.7.0 get-stream: 6.0.1 is-stream: 2.0.1 - isbinaryfile: 5.0.6 + isbinaryfile: 5.0.7 koa: 2.16.3 koa-etag: 4.0.0 koa-send: 5.0.1 @@ -12508,7 +13091,7 @@ snapshots: '@web/test-runner-core': 0.13.4(bufferutil@4.0.9) '@web/test-runner-coverage-v8': 0.8.0(bufferutil@4.0.9) chrome-launcher: 0.15.2 - puppeteer-core: 24.25.0(bufferutil@4.0.9) + puppeteer-core: 24.31.0(bufferutil@4.0.9) transitivePeerDependencies: - bare-abort-controller - bare-buffer @@ -12694,7 +13277,7 @@ snapshots: jsonparse: 1.3.1 through: 2.3.8 - abbrev@3.0.1: {} + abbrev@4.0.0: {} abort-controller@3.0.0: dependencies: @@ -12707,7 +13290,7 @@ snapshots: accepts@2.0.0: dependencies: - mime-types: 3.0.1 + mime-types: 3.0.2 negotiator: 1.0.0 acorn-import-phases@1.0.4(acorn@8.15.0): @@ -12793,7 +13376,7 @@ snapshots: dependencies: type-fest: 0.21.3 - ansi-escapes@7.1.1: + ansi-escapes@7.2.0: dependencies: environment: 1.1.0 @@ -12902,11 +13485,13 @@ snapshots: assert-plus@1.0.0: {} + assertion-error@2.0.1: {} + ast-types@0.13.4: dependencies: tslib: 2.8.1 - ast-v8-to-istanbul@0.3.7: + ast-v8-to-istanbul@0.3.8: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -12930,8 +13515,8 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.26.3 - caniuse-lite: 1.0.30001751 + browserslist: 4.28.0 + caniuse-lite: 1.0.30001757 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -12948,15 +13533,15 @@ snapshots: b4a@1.7.3: {} - babel-loader@10.0.0(@babel/core@7.28.4)(webpack@5.102.1(esbuild@0.25.11)): + babel-loader@10.0.0(@babel/core@7.28.4)(webpack@5.102.1(esbuild@0.26.0)): dependencies: '@babel/core': 7.28.4 find-up: 5.0.0 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4): dependencies: - '@babel/compat-data': 7.28.4 + '@babel/compat-data': 7.28.5 '@babel/core': 7.28.4 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) semver: 6.3.1 @@ -12967,7 +13552,7 @@ snapshots: dependencies: '@babel/core': 7.28.4 '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4) - core-js-compat: 3.46.0 + core-js-compat: 3.47.0 transitivePeerDependencies: - supports-color @@ -12980,14 +13565,14 @@ snapshots: balanced-match@1.0.2: {} - bare-events@2.8.0: {} + bare-events@2.8.2: {} - bare-fs@4.4.11: + bare-fs@4.5.1: dependencies: - bare-events: 2.8.0 + bare-events: 2.8.2 bare-path: 3.0.0 - bare-stream: 2.7.0(bare-events@2.8.0) - bare-url: 2.3.0 + bare-stream: 2.7.0(bare-events@2.8.2) + bare-url: 2.3.2 fast-fifo: 1.3.2 transitivePeerDependencies: - bare-abort-controller @@ -13002,17 +13587,17 @@ snapshots: bare-os: 3.6.2 optional: true - bare-stream@2.7.0(bare-events@2.8.0): + bare-stream@2.7.0(bare-events@2.8.2): dependencies: streamx: 2.23.0 optionalDependencies: - bare-events: 2.8.0 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller - react-native-b4a optional: true - bare-url@2.3.0: + bare-url@2.3.2: dependencies: bare-path: 3.0.0 optional: true @@ -13021,7 +13606,7 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.8.18: {} + baseline-browser-mapping@2.8.31: {} basic-ftp@5.0.5: {} @@ -13085,16 +13670,16 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@2.2.0: + body-parser@2.2.1: dependencies: bytes: 3.1.2 content-type: 1.0.5 debug: 4.4.3(supports-color@10.2.2) - http-errors: 2.0.0 - iconv-lite: 0.6.3 + http-errors: 2.0.1 + iconv-lite: 0.7.0 on-finished: 2.4.1 qs: 6.14.0 - raw-body: 3.0.1 + raw-body: 3.0.2 type-is: 2.0.1 transitivePeerDependencies: - supports-color @@ -13127,24 +13712,24 @@ snapshots: fresh: 0.5.2 mitt: 1.2.0 - browser-sync-ui@3.0.4(bufferutil@4.0.9): + browser-sync-ui@3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: async-each-series: 0.1.1 chalk: 4.1.2 connect-history-api-fallback: 1.6.0 immutable: 3.8.2 server-destroy: 1.0.1 - socket.io-client: 4.8.1(bufferutil@4.0.9) + socket.io-client: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) stream-throttle: 0.1.3 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - browser-sync@3.0.4(bufferutil@4.0.9): + browser-sync@3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: browser-sync-client: 3.0.4 - browser-sync-ui: 3.0.4(bufferutil@4.0.9) + browser-sync-ui: 3.0.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) bs-recipes: 1.3.4 chalk: 4.1.2 chokidar: 3.6.0 @@ -13168,7 +13753,7 @@ snapshots: serve-index: 1.9.1 serve-static: 1.16.2 server-destroy: 1.0.1 - socket.io: 4.8.1(bufferutil@4.0.9) + socket.io: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) ua-parser-js: 1.0.41 yargs: 17.7.2 transitivePeerDependencies: @@ -13181,13 +13766,13 @@ snapshots: dependencies: pako: 0.2.9 - browserslist@4.26.3: + browserslist@4.28.0: dependencies: - baseline-browser-mapping: 2.8.18 - caniuse-lite: 1.0.30001751 - electron-to-chromium: 1.5.237 - node-releases: 2.0.25 - update-browserslist-db: 1.1.3(browserslist@4.26.3) + baseline-browser-mapping: 2.8.31 + caniuse-lite: 1.0.30001757 + electron-to-chromium: 1.5.260 + node-releases: 2.0.27 + update-browserslist-db: 1.1.4(browserslist@4.28.0) browserstack@1.6.1: dependencies: @@ -13223,34 +13808,19 @@ snapshots: bytes@3.1.2: {} - cacache@19.0.1: - dependencies: - '@npmcli/fs': 4.0.0 - fs-minipass: 3.0.3 - glob: 10.4.5 - lru-cache: 10.4.3 - minipass: 7.1.2 - minipass-collect: 2.0.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - p-map: 7.0.3 - ssri: 12.0.0 - tar: 7.5.1 - unique-filename: 4.0.0 - - cacache@20.0.1: + cacache@20.0.3: dependencies: - '@npmcli/fs': 4.0.0 + '@npmcli/fs': 5.0.0 fs-minipass: 3.0.3 - glob: 11.0.3 + glob: 13.0.0 lru-cache: 11.2.2 minipass: 7.1.2 minipass-collect: 2.0.1 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 - p-map: 7.0.3 - ssri: 12.0.0 - unique-filename: 4.0.0 + p-map: 7.0.4 + ssri: 13.0.0 + unique-filename: 5.0.0 cache-content-type@1.0.1: dependencies: @@ -13292,11 +13862,11 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001751: {} + caniuse-lite@1.0.30001757: {} caseless@0.12.0: {} - chai@6.2.0: {} + chai@6.2.1: {} chalk-template@0.4.0: dependencies: @@ -13317,11 +13887,11 @@ snapshots: chalk@5.6.2: {} - chardet@2.1.0: {} + chardet@2.1.1: {} checkpoint-stream@0.1.2: dependencies: - '@types/pumpify': 1.4.4 + '@types/pumpify': 1.4.5 events-intercept: 2.0.0 pumpify: 1.5.1 split-array-stream: 1.0.3 @@ -13349,7 +13919,7 @@ snapshots: chrome-launcher@0.15.2: dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 escape-string-regexp: 4.0.0 is-wsl: 2.2.0 lighthouse-logger: 1.4.2 @@ -13358,9 +13928,9 @@ snapshots: chrome-trace-event@1.0.4: {} - chromium-bidi@9.1.0(devtools-protocol@0.0.1508733): + chromium-bidi@11.0.0(devtools-protocol@0.0.1521046): dependencies: - devtools-protocol: 0.0.1508733 + devtools-protocol: 0.0.1521046 mitt: 3.0.1 zod: 3.25.76 @@ -13378,7 +13948,7 @@ snapshots: cli-spinners@3.3.0: {} - cli-truncate@5.1.0: + cli-truncate@5.1.1: dependencies: slice-ansi: 7.1.2 string-width: 8.1.0 @@ -13465,7 +14035,7 @@ snapshots: table-layout: 4.1.1 typical: 7.3.0 - commander@14.0.1: {} + commander@14.0.2: {} commander@2.20.3: {} @@ -13519,9 +14089,7 @@ snapshots: dependencies: safe-buffer: 5.2.1 - content-disposition@1.0.0: - dependencies: - safe-buffer: 5.2.1 + content-disposition@1.0.1: {} content-type@1.0.5: {} @@ -13552,18 +14120,18 @@ snapshots: dependencies: is-what: 3.14.1 - copy-webpack-plugin@13.0.1(webpack@5.102.1(esbuild@0.25.11)): + copy-webpack-plugin@13.0.1(webpack@5.102.1(esbuild@0.26.0)): 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.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) - core-js-compat@3.46.0: + core-js-compat@3.47.0: dependencies: - browserslist: 4.26.3 + browserslist: 4.28.0 core-util-is@1.0.2: {} @@ -13578,7 +14146,7 @@ snapshots: dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 parse-json: 5.2.0 optionalDependencies: typescript: 5.9.3 @@ -13603,7 +14171,7 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - css-loader@7.1.2(webpack@5.102.1(esbuild@0.25.11)): + css-loader@7.1.2(webpack@5.102.1(esbuild@0.26.0)): dependencies: icss-utils: 5.1.0(postcss@8.5.6) postcss: 8.5.6 @@ -13614,7 +14182,7 @@ snapshots: postcss-value-parser: 4.2.0 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) css-select@6.0.0: dependencies: @@ -13633,13 +14201,11 @@ snapshots: cssesc@3.0.0: {} - cssstyle@5.3.1(postcss@8.5.6): + cssstyle@5.3.3: dependencies: - '@asamuzakjp/css-color': 4.0.5 - '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6) + '@asamuzakjp/css-color': 4.1.0 + '@csstools/css-syntax-patches-for-csstree': 1.0.17 css-tree: 3.1.0 - transitivePeerDependencies: - - postcss custom-event@1.0.1: {} @@ -13726,12 +14292,12 @@ snapshots: deepmerge@4.3.1: {} - default-browser-id@5.0.0: {} + default-browser-id@5.0.1: {} - default-browser@5.2.1: + default-browser@5.4.0: dependencies: bundle-name: 4.1.0 - default-browser-id: 5.0.0 + default-browser-id: 5.0.1 default-gateway@6.0.3: dependencies: @@ -13799,7 +14365,7 @@ snapshots: devtools-protocol@0.0.1045489: {} - devtools-protocol@0.0.1508733: {} + devtools-protocol@0.0.1521046: {} di@0.0.1: {} @@ -13889,7 +14455,7 @@ snapshots: dependencies: jake: 10.9.4 - electron-to-chromium@1.5.237: {} + electron-to-chromium@1.5.260: {} emoji-regex@10.6.0: {} @@ -13911,12 +14477,12 @@ snapshots: dependencies: once: 1.4.0 - engine.io-client@6.6.3(bufferutil@4.0.9): + engine.io-client@6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) xmlhttprequest-ssl: 2.1.2 transitivePeerDependencies: - bufferutil @@ -13925,17 +14491,17 @@ snapshots: engine.io-parser@5.2.3: {} - engine.io@6.6.4(bufferutil@4.0.9): + engine.io@6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@types/cors': 2.8.19 - '@types/node': 22.18.11 + '@types/node': 22.19.1 accepts: 1.3.8 base64id: 2.0.0 cookie: 0.7.2 cors: 2.8.5 debug: 4.3.7 engine.io-parser: 5.2.3 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil - supports-color @@ -14066,36 +14632,94 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild-wasm@0.25.11: {} + esbuild-wasm@0.26.0: {} - esbuild@0.25.11: + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + esbuild@0.26.0: + optionalDependencies: + '@esbuild/aix-ppc64': 0.26.0 + '@esbuild/android-arm': 0.26.0 + '@esbuild/android-arm64': 0.26.0 + '@esbuild/android-x64': 0.26.0 + '@esbuild/darwin-arm64': 0.26.0 + '@esbuild/darwin-x64': 0.26.0 + '@esbuild/freebsd-arm64': 0.26.0 + '@esbuild/freebsd-x64': 0.26.0 + '@esbuild/linux-arm': 0.26.0 + '@esbuild/linux-arm64': 0.26.0 + '@esbuild/linux-ia32': 0.26.0 + '@esbuild/linux-loong64': 0.26.0 + '@esbuild/linux-mips64el': 0.26.0 + '@esbuild/linux-ppc64': 0.26.0 + '@esbuild/linux-riscv64': 0.26.0 + '@esbuild/linux-s390x': 0.26.0 + '@esbuild/linux-x64': 0.26.0 + '@esbuild/netbsd-arm64': 0.26.0 + '@esbuild/netbsd-x64': 0.26.0 + '@esbuild/openbsd-arm64': 0.26.0 + '@esbuild/openbsd-x64': 0.26.0 + '@esbuild/openharmony-arm64': 0.26.0 + '@esbuild/sunos-x64': 0.26.0 + '@esbuild/win32-arm64': 0.26.0 + '@esbuild/win32-ia32': 0.26.0 + '@esbuild/win32-x64': 0.26.0 + + esbuild@0.27.0: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.11 - '@esbuild/android-arm': 0.25.11 - '@esbuild/android-arm64': 0.25.11 - '@esbuild/android-x64': 0.25.11 - '@esbuild/darwin-arm64': 0.25.11 - '@esbuild/darwin-x64': 0.25.11 - '@esbuild/freebsd-arm64': 0.25.11 - '@esbuild/freebsd-x64': 0.25.11 - '@esbuild/linux-arm': 0.25.11 - '@esbuild/linux-arm64': 0.25.11 - '@esbuild/linux-ia32': 0.25.11 - '@esbuild/linux-loong64': 0.25.11 - '@esbuild/linux-mips64el': 0.25.11 - '@esbuild/linux-ppc64': 0.25.11 - '@esbuild/linux-riscv64': 0.25.11 - '@esbuild/linux-s390x': 0.25.11 - '@esbuild/linux-x64': 0.25.11 - '@esbuild/netbsd-arm64': 0.25.11 - '@esbuild/netbsd-x64': 0.25.11 - '@esbuild/openbsd-arm64': 0.25.11 - '@esbuild/openbsd-x64': 0.25.11 - '@esbuild/openharmony-arm64': 0.25.11 - '@esbuild/sunos-x64': 0.25.11 - '@esbuild/win32-arm64': 0.25.11 - '@esbuild/win32-ia32': 0.25.11 - '@esbuild/win32-x64': 0.25.11 + '@esbuild/aix-ppc64': 0.27.0 + '@esbuild/android-arm': 0.27.0 + '@esbuild/android-arm64': 0.27.0 + '@esbuild/android-x64': 0.27.0 + '@esbuild/darwin-arm64': 0.27.0 + '@esbuild/darwin-x64': 0.27.0 + '@esbuild/freebsd-arm64': 0.27.0 + '@esbuild/freebsd-x64': 0.27.0 + '@esbuild/linux-arm': 0.27.0 + '@esbuild/linux-arm64': 0.27.0 + '@esbuild/linux-ia32': 0.27.0 + '@esbuild/linux-loong64': 0.27.0 + '@esbuild/linux-mips64el': 0.27.0 + '@esbuild/linux-ppc64': 0.27.0 + '@esbuild/linux-riscv64': 0.27.0 + '@esbuild/linux-s390x': 0.27.0 + '@esbuild/linux-x64': 0.27.0 + '@esbuild/netbsd-arm64': 0.27.0 + '@esbuild/netbsd-x64': 0.27.0 + '@esbuild/openbsd-arm64': 0.27.0 + '@esbuild/openbsd-x64': 0.27.0 + '@esbuild/openharmony-arm64': 0.27.0 + '@esbuild/sunos-x64': 0.27.0 + '@esbuild/win32-arm64': 0.27.0 + '@esbuild/win32-ia32': 0.27.0 + '@esbuild/win32-x64': 0.27.0 escalade@3.2.0: {} @@ -14185,13 +14809,13 @@ snapshots: eslint@9.38.0(jiti@2.6.1): dependencies: '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) - '@eslint-community/regexpp': 4.12.1 + '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.1 - '@eslint/config-helpers': 0.4.1 + '@eslint/config-helpers': 0.4.2 '@eslint/core': 0.16.0 '@eslint/eslintrc': 3.3.1 '@eslint/js': 9.38.0 - '@eslint/plugin-kit': 0.4.0 + '@eslint/plugin-kit': 0.4.1 '@humanfs/node': 0.16.7 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 @@ -14263,7 +14887,7 @@ snapshots: events-universal@1.0.1: dependencies: - bare-events: 2.8.0 + bare-events: 2.8.2 transitivePeerDependencies: - bare-abort-controller @@ -14338,8 +14962,8 @@ snapshots: express@5.1.0: dependencies: accepts: 2.0.0 - body-parser: 2.2.0 - content-disposition: 1.0.0 + body-parser: 2.2.1 + content-disposition: 1.0.1 content-type: 1.0.5 cookie: 0.7.2 cookie-signature: 1.2.2 @@ -14349,9 +14973,9 @@ snapshots: etag: 1.8.1 finalhandler: 2.1.0 fresh: 2.0.0 - http-errors: 2.0.0 + http-errors: 2.0.1 merge-descriptors: 2.0.0 - mime-types: 3.0.1 + mime-types: 3.0.2 on-finished: 2.4.1 once: 1.4.0 parseurl: 1.3.3 @@ -14504,35 +15128,35 @@ snapshots: locate-path: 6.0.0 path-exists: 4.0.0 - firebase@12.4.0: + firebase@12.6.0: dependencies: - '@firebase/ai': 2.4.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.4) - '@firebase/analytics': 0.10.19(@firebase/app@0.14.4) - '@firebase/analytics-compat': 0.2.25(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/app': 0.14.4 - '@firebase/app-check': 0.11.0(@firebase/app@0.14.4) - '@firebase/app-check-compat': 0.4.0(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/app-compat': 0.5.4 + '@firebase/ai': 2.6.0(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/analytics': 0.10.19(@firebase/app@0.14.6) + '@firebase/analytics-compat': 0.2.25(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/app': 0.14.6 + '@firebase/app-check': 0.11.0(@firebase/app@0.14.6) + '@firebase/app-check-compat': 0.4.0(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/app-compat': 0.5.6 '@firebase/app-types': 0.9.3 - '@firebase/auth': 1.11.0(@firebase/app@0.14.4) - '@firebase/auth-compat': 0.6.0(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4) - '@firebase/data-connect': 0.3.11(@firebase/app@0.14.4) + '@firebase/auth': 1.11.1(@firebase/app@0.14.6) + '@firebase/auth-compat': 0.6.1(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/data-connect': 0.3.12(@firebase/app@0.14.6) '@firebase/database': 1.1.0 '@firebase/database-compat': 2.1.0 - '@firebase/firestore': 4.9.2(@firebase/app@0.14.4) - '@firebase/firestore-compat': 0.4.2(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4) - '@firebase/functions': 0.13.1(@firebase/app@0.14.4) - '@firebase/functions-compat': 0.4.1(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/installations': 0.6.19(@firebase/app@0.14.4) - '@firebase/installations-compat': 0.2.19(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4) - '@firebase/messaging': 0.12.23(@firebase/app@0.14.4) - '@firebase/messaging-compat': 0.2.23(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/performance': 0.7.9(@firebase/app@0.14.4) - '@firebase/performance-compat': 0.2.22(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/remote-config': 0.7.0(@firebase/app@0.14.4) - '@firebase/remote-config-compat': 0.2.20(@firebase/app-compat@0.5.4)(@firebase/app@0.14.4) - '@firebase/storage': 0.14.0(@firebase/app@0.14.4) - '@firebase/storage-compat': 0.4.0(@firebase/app-compat@0.5.4)(@firebase/app-types@0.9.3)(@firebase/app@0.14.4) + '@firebase/firestore': 4.9.2(@firebase/app@0.14.6) + '@firebase/firestore-compat': 0.4.2(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/functions': 0.13.1(@firebase/app@0.14.6) + '@firebase/functions-compat': 0.4.1(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/installations': 0.6.19(@firebase/app@0.14.6) + '@firebase/installations-compat': 0.2.19(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) + '@firebase/messaging': 0.12.23(@firebase/app@0.14.6) + '@firebase/messaging-compat': 0.2.23(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/performance': 0.7.9(@firebase/app@0.14.6) + '@firebase/performance-compat': 0.2.22(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/remote-config': 0.7.0(@firebase/app@0.14.6) + '@firebase/remote-config-compat': 0.2.20(@firebase/app-compat@0.5.6)(@firebase/app@0.14.6) + '@firebase/storage': 0.14.0(@firebase/app@0.14.6) + '@firebase/storage-compat': 0.4.0(@firebase/app-compat@0.5.6)(@firebase/app-types@0.9.3)(@firebase/app@0.14.6) '@firebase/util': 1.13.0 transitivePeerDependencies: - '@react-native-async-storage/async-storage' @@ -14576,7 +15200,7 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 - form-data@4.0.4: + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 @@ -14632,18 +15256,19 @@ snapshots: functions-have-names@1.2.3: {} - gaxios@7.1.2(supports-color@10.2.2): + gaxios@7.1.3(supports-color@10.2.2): dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6(supports-color@10.2.2) node-fetch: 3.3.2 + rimraf: 5.0.10 transitivePeerDependencies: - supports-color - gcp-metadata@8.1.1(supports-color@10.2.2): + gcp-metadata@8.1.2(supports-color@10.2.2): dependencies: - gaxios: 7.1.2(supports-color@10.2.2) - google-logging-utils: 1.1.1 + gaxios: 7.1.3(supports-color@10.2.2) + google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: - supports-color @@ -14688,7 +15313,7 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - get-tsconfig@4.12.0: + get-tsconfig@4.13.0: dependencies: resolve-pkg-maps: 1.0.0 @@ -14726,7 +15351,7 @@ snapshots: glob-to-regexp@0.4.1: {} - glob@10.4.5: + glob@10.5.0: dependencies: foreground-child: 3.3.1 jackspeak: 3.4.3 @@ -14735,14 +15360,11 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 - glob@11.0.3: + glob@13.0.0: dependencies: - foreground-child: 3.3.1 - jackspeak: 4.1.1 - minimatch: 10.0.3 + minimatch: 10.1.1 minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 2.0.0 + path-scurry: 2.0.1 glob@7.2.3: dependencies: @@ -14780,34 +15402,35 @@ snapshots: pify: 2.3.0 pinkie-promise: 2.0.1 - google-auth-library@10.4.1(supports-color@10.2.2): + google-auth-library@10.5.0(supports-color@10.2.2): dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.2(supports-color@10.2.2) - gcp-metadata: 8.1.1(supports-color@10.2.2) - google-logging-utils: 1.1.1 + gaxios: 7.1.3(supports-color@10.2.2) + gcp-metadata: 8.1.2(supports-color@10.2.2) + google-logging-utils: 1.1.3 gtoken: 8.0.0(supports-color@10.2.2) jws: 4.0.0 transitivePeerDependencies: - supports-color - google-gax@5.0.4(supports-color@10.2.2): + google-gax@5.0.6(supports-color@10.2.2): dependencies: - '@grpc/grpc-js': 1.14.0 + '@grpc/grpc-js': 1.14.1 '@grpc/proto-loader': 0.8.0 duplexify: 4.1.3 - google-auth-library: 10.4.1(supports-color@10.2.2) - google-logging-utils: 1.1.1 + google-auth-library: 10.5.0(supports-color@10.2.2) + google-logging-utils: 1.1.3 node-fetch: 3.3.2 object-hash: 3.0.0 - proto3-json-serializer: 3.0.3 + proto3-json-serializer: 3.0.4 protobufjs: 7.5.4 retry-request: 8.0.2(supports-color@10.2.2) + rimraf: 5.0.10 transitivePeerDependencies: - supports-color - google-logging-utils@1.1.1: {} + google-logging-utils@1.1.3: {} gopd@1.2.0: {} @@ -14830,21 +15453,21 @@ snapshots: graphemer@1.4.0: {} - graphql-tag@2.12.6(graphql@16.11.0): + graphql-tag@2.12.6(graphql@16.12.0): dependencies: - graphql: 16.11.0 + graphql: 16.12.0 tslib: 2.8.1 - graphql@16.11.0: {} + graphql@16.12.0: {} grpc-gcp@1.0.1(protobufjs@7.5.4): dependencies: - '@grpc/grpc-js': 1.14.0 + '@grpc/grpc-js': 1.14.1 protobufjs: 7.5.4 gtoken@8.0.0(supports-color@10.2.2): dependencies: - gaxios: 7.1.2(supports-color@10.2.2) + gaxios: 7.1.3(supports-color@10.2.2) jws: 4.0.0 transitivePeerDependencies: - supports-color @@ -14960,6 +15583,14 @@ snapshots: statuses: 2.0.1 toidentifier: 1.0.1 + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + http-parser-js@0.5.10: {} http-proxy-agent@5.0.0(supports-color@10.2.2): @@ -14977,21 +15608,21 @@ snapshots: transitivePeerDependencies: - supports-color - http-proxy-middleware@2.0.9(@types/express@4.17.23): + http-proxy-middleware@2.0.9(@types/express@4.17.25): dependencies: - '@types/http-proxy': 1.17.16 + '@types/http-proxy': 1.17.17 http-proxy: 1.18.1(debug@4.4.3) is-glob: 4.0.3 is-plain-obj: 3.0.0 micromatch: 4.0.8 optionalDependencies: - '@types/express': 4.17.23 + '@types/express': 4.17.25 transitivePeerDependencies: - debug http-proxy-middleware@3.0.5: dependencies: - '@types/http-proxy': 1.17.16 + '@types/http-proxy': 1.17.17 debug: 4.4.3(supports-color@10.2.2) http-proxy: 1.18.1(debug@4.4.3) is-glob: 4.0.3 @@ -15076,7 +15707,7 @@ snapshots: ignore-walk@8.0.0: dependencies: - minimatch: 10.0.3 + minimatch: 10.1.1 ignore@5.3.2: {} @@ -15113,7 +15744,9 @@ snapshots: ini@5.0.0: {} - injection-js@2.6.0: + ini@6.0.0: {} + + injection-js@2.6.1: dependencies: tslib: 2.8.1 @@ -15130,7 +15763,7 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 - ip-address@10.0.1: {} + ip-address@10.1.0: {} ip-regex@4.3.0: {} @@ -15344,7 +15977,7 @@ snapshots: isbinaryfile@4.0.10: {} - isbinaryfile@5.0.6: {} + isbinaryfile@5.0.7: {} isexe@2.0.0: {} @@ -15359,7 +15992,7 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 @@ -15369,7 +16002,7 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.28.4 - '@babel/parser': 7.28.4 + '@babel/parser': 7.28.5 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.7.3 @@ -15409,10 +16042,6 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.1.1: - dependencies: - '@isaacs/cliui': 8.0.2 - jake@10.9.4: dependencies: async: 3.2.6 @@ -15423,7 +16052,7 @@ snapshots: jasmine-core@4.6.1: {} - jasmine-core@5.12.0: {} + jasmine-core@5.12.1: {} jasmine-reporters@2.5.2: dependencies: @@ -15442,14 +16071,14 @@ snapshots: jasmine@5.12.0: dependencies: - glob: 10.4.5 - jasmine-core: 5.12.0 + glob: 10.5.0 + jasmine-core: 5.12.1 jasminewd2@2.2.0: {} jest-worker@27.5.1: dependencies: - '@types/node': 22.18.11 + '@types/node': 22.19.1 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -15465,12 +16094,17 @@ snapshots: dependencies: argparse: 2.0.1 + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsbn@0.1.1: {} - jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5): + jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: - '@asamuzakjp/dom-selector': 6.7.2 - cssstyle: 5.3.1(postcss@8.5.6) + '@acemir/cssom': 0.9.24 + '@asamuzakjp/dom-selector': 6.7.4 + cssstyle: 5.3.3 data-urls: 6.0.0 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 @@ -15478,7 +16112,6 @@ snapshots: https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 parse5: 8.0.0 - rrweb-cssom: 0.8.0 saxes: 6.0.0 symbol-tree: 3.2.4 tough-cookie: 6.0.0 @@ -15491,7 +16124,6 @@ snapshots: xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil - - postcss - supports-color - utf-8-validate @@ -15505,7 +16137,7 @@ snapshots: json-parse-even-better-errors@2.3.1: {} - json-parse-even-better-errors@4.0.0: {} + json-parse-even-better-errors@5.0.0: {} json-schema-traverse@0.4.1: {} @@ -15606,9 +16238,9 @@ snapshots: transitivePeerDependencies: - supports-color - karma-jasmine-html-reporter@2.1.0(jasmine-core@5.12.0)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)))(karma@6.4.4(bufferutil@4.0.9)): + karma-jasmine-html-reporter@2.1.0(jasmine-core@5.12.1)(karma-jasmine@5.1.0(karma@6.4.4(bufferutil@4.0.9)))(karma@6.4.4(bufferutil@4.0.9)): dependencies: - jasmine-core: 5.12.0 + jasmine-core: 5.12.1 karma: 6.4.4(bufferutil@4.0.9) karma-jasmine: 5.1.0(karma@6.4.4(bufferutil@4.0.9)) @@ -15642,7 +16274,7 @@ snapshots: qjobs: 1.2.0 range-parser: 1.2.1 rimraf: 3.0.2 - socket.io: 4.8.1(bufferutil@4.0.9) + socket.io: 4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) source-map: 0.6.1 tmp: 0.2.5 ua-parser-js: 0.7.41 @@ -15717,16 +16349,16 @@ snapshots: transitivePeerDependencies: - supports-color - launch-editor@2.11.1: + launch-editor@2.12.0: dependencies: picocolors: 1.1.1 shell-quote: 1.8.3 - less-loader@12.3.0(less@4.4.2)(webpack@5.102.1(esbuild@0.25.11)): + less-loader@12.3.0(less@4.4.2)(webpack@5.102.1(esbuild@0.26.0)): dependencies: less: 4.4.2 optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) less@4.4.2: dependencies: @@ -15747,11 +16379,11 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - license-webpack-plugin@4.0.2(webpack@5.102.1(esbuild@0.25.11)): + license-webpack-plugin@4.0.2(webpack@5.102.1(esbuild@0.26.0)): dependencies: webpack-sources: 3.3.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) lie@3.3.0: dependencies: @@ -15770,7 +16402,7 @@ snapshots: listr2@9.0.5: dependencies: - cli-truncate: 5.1.0 + cli-truncate: 5.1.1 colorette: 2.0.20 eventemitter3: 5.0.1 log-update: 6.1.0 @@ -15856,7 +16488,7 @@ snapshots: log-update@6.1.0: dependencies: - ansi-escapes: 7.1.1 + ansi-escapes: 7.2.0 cli-cursor: 5.0.0 slice-ansi: 7.1.2 strip-ansi: 7.1.2 @@ -15900,10 +16532,14 @@ snapshots: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - magicast@0.3.5: + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magicast@0.5.1: dependencies: - '@babel/parser': 7.28.4 - '@babel/types': 7.28.4 + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 source-map-js: 1.2.1 make-dir@2.1.0: @@ -15918,35 +16554,19 @@ snapshots: make-error@1.3.6: {} - make-fetch-happen@14.0.3: - dependencies: - '@npmcli/agent': 3.0.0 - cacache: 19.0.1 - http-cache-semantics: 4.2.0 - minipass: 7.1.2 - minipass-fetch: 4.0.1 - minipass-flush: 1.0.5 - minipass-pipeline: 1.2.4 - negotiator: 1.0.0 - proc-log: 5.0.0 - promise-retry: 2.0.1 - ssri: 12.0.0 - transitivePeerDependencies: - - supports-color - - make-fetch-happen@15.0.2: + make-fetch-happen@15.0.3: dependencies: '@npmcli/agent': 4.0.0 - cacache: 20.0.1 + cacache: 20.0.3 http-cache-semantics: 4.2.0 minipass: 7.1.2 - minipass-fetch: 4.0.1 + minipass-fetch: 5.0.0 minipass-flush: 1.0.5 minipass-pipeline: 1.2.4 negotiator: 1.0.0 - proc-log: 5.0.0 + proc-log: 6.0.0 promise-retry: 2.0.1 - ssri: 12.0.0 + ssri: 13.0.0 transitivePeerDependencies: - supports-color @@ -15960,7 +16580,7 @@ snapshots: media-typer@1.1.0: {} - memfs@4.49.0: + memfs@4.51.0: dependencies: '@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1) '@jsonjoy.com/util': 1.9.0(tslib@2.8.1) @@ -15994,7 +16614,7 @@ snapshots: dependencies: mime-db: 1.52.0 - mime-types@3.0.1: + mime-types@3.0.2: dependencies: mime-db: 1.54.0 @@ -16012,15 +16632,15 @@ snapshots: mimic-response@3.1.0: {} - mini-css-extract-plugin@2.9.4(webpack@5.102.1(esbuild@0.25.11)): + mini-css-extract-plugin@2.9.4(webpack@5.102.1(esbuild@0.26.0)): dependencies: schema-utils: 4.3.3 tapable: 2.3.0 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) minimalistic-assert@1.0.1: {} - minimatch@10.0.3: + minimatch@10.1.1: dependencies: '@isaacs/brace-expansion': 5.0.0 @@ -16046,7 +16666,7 @@ snapshots: dependencies: minipass: 7.1.2 - minipass-fetch@4.0.1: + minipass-fetch@5.0.0: dependencies: minipass: 7.1.2 minipass-sized: 1.0.3 @@ -16126,6 +16746,8 @@ snapshots: mute-stream@2.0.0: {} + mute-stream@3.0.0: {} + nanocolors@0.2.13: {} nanoid@3.3.11: {} @@ -16135,7 +16757,7 @@ snapshots: needle@3.3.1: dependencies: iconv-lite: 0.6.3 - sax: 1.4.1 + sax: 1.4.3 optional: true negotiator@0.6.3: {} @@ -16148,34 +16770,34 @@ snapshots: netmask@2.0.2: {} - ng-packagr@21.0.0-next.4(@angular/compiler-cli@21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3): + ng-packagr@21.0.0(@angular/compiler-cli@21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3))(tslib@2.8.1)(typescript@5.9.3): dependencies: '@ampproject/remapping': 2.3.0 - '@angular/compiler-cli': 21.0.0-next.9(@angular/compiler@21.0.0-next.9)(typescript@5.9.3) - '@rollup/plugin-json': 6.1.0(rollup@4.52.4) - '@rollup/wasm-node': 4.52.5 + '@angular/compiler-cli': 21.0.1(@angular/compiler@21.0.1)(typescript@5.9.3) + '@rollup/plugin-json': 6.1.0(rollup@4.52.5) + '@rollup/wasm-node': 4.53.3 ajv: 8.17.1 ansi-colors: 4.1.3 - browserslist: 4.26.3 + browserslist: 4.28.0 chokidar: 4.0.3 - commander: 14.0.1 + commander: 14.0.2 dependency-graph: 1.0.0 - esbuild: 0.25.11 + esbuild: 0.27.0 find-cache-directory: 6.0.0 - injection-js: 2.6.0 + injection-js: 2.6.1 jsonc-parser: 3.3.1 less: 4.4.2 ora: 9.0.0 piscina: 5.1.3 postcss: 8.5.6 - rollup-plugin-dts: 6.2.3(rollup@4.52.4)(typescript@5.9.3) + rollup-plugin-dts: 6.2.3(rollup@4.52.5)(typescript@5.9.3) rxjs: 7.8.2 sass: 1.93.2 tinyglobby: 0.2.15 tslib: 2.8.1 typescript: 5.9.3 optionalDependencies: - rollup: 4.52.4 + rollup: 4.52.5 nock@14.0.10: dependencies: @@ -16220,26 +16842,26 @@ snapshots: node-gyp-build@4.8.4: {} - node-gyp@11.5.0: + node-gyp@12.1.0: dependencies: env-paths: 2.2.1 exponential-backoff: 3.1.3 graceful-fs: 4.2.11 - make-fetch-happen: 14.0.3 - nopt: 8.1.0 - proc-log: 5.0.0 + make-fetch-happen: 15.0.3 + nopt: 9.0.0 + proc-log: 6.0.0 semver: 7.7.3 - tar: 7.5.1 + tar: 7.5.2 tinyglobby: 0.2.15 - which: 5.0.0 + which: 6.0.0 transitivePeerDependencies: - supports-color - node-releases@2.0.25: {} + node-releases@2.0.27: {} - nopt@8.1.0: + nopt@9.0.0: dependencies: - abbrev: 3.0.1 + abbrev: 4.0.0 normalize-path@3.0.0: {} @@ -16251,12 +16873,14 @@ snapshots: dependencies: npm-normalize-package-bin: 4.0.0 - npm-install-checks@7.1.2: + npm-install-checks@8.0.0: dependencies: semver: 7.7.3 npm-normalize-package-bin@4.0.0: {} + npm-normalize-package-bin@5.0.0: {} + npm-package-arg@13.0.1: dependencies: hosted-git-info: 9.0.2 @@ -16264,28 +16888,28 @@ snapshots: semver: 7.7.3 validate-npm-package-name: 6.0.2 - npm-packlist@10.0.2: + npm-packlist@10.0.3: dependencies: ignore-walk: 8.0.0 - proc-log: 5.0.0 + proc-log: 6.0.0 - npm-pick-manifest@11.0.1: + npm-pick-manifest@11.0.3: dependencies: - npm-install-checks: 7.1.2 - npm-normalize-package-bin: 4.0.0 + npm-install-checks: 8.0.0 + npm-normalize-package-bin: 5.0.0 npm-package-arg: 13.0.1 semver: 7.7.3 - npm-registry-fetch@19.0.0: + npm-registry-fetch@19.1.1: dependencies: - '@npmcli/redact': 3.2.2 + '@npmcli/redact': 4.0.0 jsonparse: 1.3.1 - make-fetch-happen: 15.0.2 + make-fetch-happen: 15.0.3 minipass: 7.1.2 - minipass-fetch: 4.0.1 + minipass-fetch: 5.0.0 minizlib: 3.1.0 npm-package-arg: 13.0.1 - proc-log: 5.0.0 + proc-log: 6.0.0 transitivePeerDependencies: - supports-color @@ -16366,7 +16990,7 @@ snapshots: open@10.2.0: dependencies: - default-browser: 5.2.1 + default-browser: 5.4.0 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 wsl-utils: 0.1.0 @@ -16439,7 +17063,7 @@ snapshots: dependencies: p-limit: 3.1.0 - p-map@7.0.3: {} + p-map@7.0.4: {} p-queue@6.6.2: dependencies: @@ -16480,23 +17104,23 @@ snapshots: pacote@21.0.3: dependencies: - '@npmcli/git': 7.0.0 + '@npmcli/git': 7.0.1 '@npmcli/installed-package-contents': 3.0.0 - '@npmcli/package-json': 7.0.1 + '@npmcli/package-json': 7.0.4 '@npmcli/promise-spawn': 8.0.3 - '@npmcli/run-script': 10.0.0 - cacache: 20.0.1 + '@npmcli/run-script': 10.0.3 + cacache: 20.0.3 fs-minipass: 3.0.3 minipass: 7.1.2 npm-package-arg: 13.0.1 - npm-packlist: 10.0.2 - npm-pick-manifest: 11.0.1 - npm-registry-fetch: 19.0.0 + npm-packlist: 10.0.3 + npm-pick-manifest: 11.0.3 + npm-registry-fetch: 19.1.1 proc-log: 5.0.0 promise-retry: 2.0.1 sigstore: 4.0.0 ssri: 12.0.0 - tar: 7.5.1 + tar: 7.5.2 transitivePeerDependencies: - supports-color @@ -16550,7 +17174,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-scurry@2.0.0: + path-scurry@2.0.1: dependencies: lru-cache: 11.2.2 minipass: 7.1.2 @@ -16623,7 +17247,7 @@ snapshots: optionalDependencies: '@napi-rs/nice': 1.1.1 - pkce-challenge@5.0.0: {} + pkce-challenge@5.0.1: {} pkg-dir@8.0.0: dependencies: @@ -16645,14 +17269,14 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(esbuild@0.25.11)): + postcss-loader@8.2.0(postcss@8.5.6)(typescript@5.9.3)(webpack@5.102.1(esbuild@0.26.0)): dependencies: cosmiconfig: 9.0.0(typescript@5.9.3) jiti: 2.6.1 postcss: 8.5.6 semver: 7.7.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) transitivePeerDependencies: - typescript @@ -16698,6 +17322,8 @@ snapshots: proc-log@5.0.0: {} + proc-log@6.0.0: {} + process-nextick-args@2.0.1: {} process-warning@1.0.0: {} @@ -16715,7 +17341,7 @@ snapshots: propagate@2.0.1: {} - proto3-json-serializer@3.0.3: + proto3-json-serializer@3.0.4: dependencies: protobufjs: 7.5.4 @@ -16731,7 +17357,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 22.18.11 + '@types/node': 22.19.1 long: 5.3.2 protractor@7.0.0: @@ -16819,14 +17445,14 @@ snapshots: - supports-color - utf-8-validate - puppeteer-core@24.25.0(bufferutil@4.0.9): + puppeteer-core@24.31.0(bufferutil@4.0.9): dependencies: - '@puppeteer/browsers': 2.10.12 - chromium-bidi: 9.1.0(devtools-protocol@0.0.1508733) + '@puppeteer/browsers': 2.10.13 + chromium-bidi: 11.0.0(devtools-protocol@0.0.1521046) debug: 4.4.3(supports-color@10.2.2) - devtools-protocol: 0.0.1508733 + devtools-protocol: 0.0.1521046 typed-query-selector: 2.12.0 - webdriver-bidi-protocol: 0.3.7 + webdriver-bidi-protocol: 0.3.9 ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bare-abort-controller @@ -16900,10 +17526,10 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - raw-body@3.0.1: + raw-body@3.0.2: dependencies: bytes: 3.1.2 - http-errors: 2.0.0 + http-errors: 2.0.1 iconv-lite: 0.7.0 unpipe: 1.0.0 @@ -17092,25 +17718,29 @@ snapshots: dependencies: glob: 7.2.3 - rolldown@1.0.0-beta.44: + rimraf@5.0.10: dependencies: - '@oxc-project/types': 0.95.0 - '@rolldown/pluginutils': 1.0.0-beta.44 + glob: 10.5.0 + + rolldown@1.0.0-beta.47: + dependencies: + '@oxc-project/types': 0.96.0 + '@rolldown/pluginutils': 1.0.0-beta.47 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-arm64': 1.0.0-beta.44 - '@rolldown/binding-darwin-x64': 1.0.0-beta.44 - '@rolldown/binding-freebsd-x64': 1.0.0-beta.44 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.44 - '@rolldown/binding-linux-x64-musl': 1.0.0-beta.44 - '@rolldown/binding-openharmony-arm64': 1.0.0-beta.44 - '@rolldown/binding-wasm32-wasi': 1.0.0-beta.44 - '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.44 - '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.44 + '@rolldown/binding-android-arm64': 1.0.0-beta.47 + '@rolldown/binding-darwin-arm64': 1.0.0-beta.47 + '@rolldown/binding-darwin-x64': 1.0.0-beta.47 + '@rolldown/binding-freebsd-x64': 1.0.0-beta.47 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.0-beta.47 + '@rolldown/binding-linux-arm64-gnu': 1.0.0-beta.47 + '@rolldown/binding-linux-arm64-musl': 1.0.0-beta.47 + '@rolldown/binding-linux-x64-gnu': 1.0.0-beta.47 + '@rolldown/binding-linux-x64-musl': 1.0.0-beta.47 + '@rolldown/binding-openharmony-arm64': 1.0.0-beta.47 + '@rolldown/binding-wasm32-wasi': 1.0.0-beta.47 + '@rolldown/binding-win32-arm64-msvc': 1.0.0-beta.47 + '@rolldown/binding-win32-ia32-msvc': 1.0.0-beta.47 + '@rolldown/binding-win32-x64-msvc': 1.0.0-beta.47 rollup-license-plugin@3.0.2: dependencies: @@ -17118,14 +17748,6 @@ snapshots: node-fetch: 3.3.2 spdx-expression-validate: 2.0.0 - rollup-plugin-dts@6.2.3(rollup@4.52.4)(typescript@5.9.3): - dependencies: - magic-string: 0.30.19 - rollup: 4.52.4 - typescript: 5.9.3 - optionalDependencies: - '@babel/code-frame': 7.27.1 - rollup-plugin-dts@6.2.3(rollup@4.52.5)(typescript@5.9.3): dependencies: magic-string: 0.30.19 @@ -17134,40 +17756,12 @@ snapshots: optionalDependencies: '@babel/code-frame': 7.27.1 - rollup-plugin-sourcemaps2@0.5.4(@types/node@22.18.11)(rollup@4.52.5): + rollup-plugin-sourcemaps2@0.5.4(@types/node@22.19.1)(rollup@4.52.5): dependencies: '@rollup/pluginutils': 5.2.0(rollup@4.52.5) rollup: 4.52.5 optionalDependencies: - '@types/node': 22.18.11 - - rollup@4.52.4: - dependencies: - '@types/estree': 1.0.8 - optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.52.4 - '@rollup/rollup-android-arm64': 4.52.4 - '@rollup/rollup-darwin-arm64': 4.52.4 - '@rollup/rollup-darwin-x64': 4.52.4 - '@rollup/rollup-freebsd-arm64': 4.52.4 - '@rollup/rollup-freebsd-x64': 4.52.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.52.4 - '@rollup/rollup-linux-arm-musleabihf': 4.52.4 - '@rollup/rollup-linux-arm64-gnu': 4.52.4 - '@rollup/rollup-linux-arm64-musl': 4.52.4 - '@rollup/rollup-linux-loong64-gnu': 4.52.4 - '@rollup/rollup-linux-ppc64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-gnu': 4.52.4 - '@rollup/rollup-linux-riscv64-musl': 4.52.4 - '@rollup/rollup-linux-s390x-gnu': 4.52.4 - '@rollup/rollup-linux-x64-gnu': 4.52.4 - '@rollup/rollup-linux-x64-musl': 4.52.4 - '@rollup/rollup-openharmony-arm64': 4.52.4 - '@rollup/rollup-win32-arm64-msvc': 4.52.4 - '@rollup/rollup-win32-ia32-msvc': 4.52.4 - '@rollup/rollup-win32-x64-gnu': 4.52.4 - '@rollup/rollup-win32-x64-msvc': 4.52.4 - fsevents: 2.3.3 + '@types/node': 22.19.1 rollup@4.52.5: dependencies: @@ -17207,8 +17801,6 @@ snapshots: transitivePeerDependencies: - supports-color - rrweb-cssom@0.8.0: {} - run-applescript@7.1.0: {} run-parallel@1.2.0: @@ -17248,12 +17840,12 @@ snapshots: safer-buffer@2.1.2: {} - sass-loader@16.0.5(sass@1.93.2)(webpack@5.102.1(esbuild@0.25.11)): + sass-loader@16.0.5(sass@1.93.2)(webpack@5.102.1(esbuild@0.26.0)): dependencies: neo-async: 2.6.2 optionalDependencies: sass: 1.93.2 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) sass@1.93.2: dependencies: @@ -17269,7 +17861,7 @@ snapshots: transitivePeerDependencies: - supports-color - sax@1.4.1: {} + sax@1.4.3: {} saxes@6.0.0: dependencies: @@ -17347,8 +17939,8 @@ snapshots: escape-html: 1.0.3 etag: 1.8.1 fresh: 2.0.0 - http-errors: 2.0.0 - mime-types: 3.0.1 + http-errors: 2.0.1 + mime-types: 3.0.2 ms: 2.1.3 on-finished: 2.4.1 range-parser: 1.2.1 @@ -17494,20 +18086,20 @@ snapshots: smart-buffer@4.2.0: {} - socket.io-adapter@2.5.5(bufferutil@4.0.9): + socket.io-adapter@2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: debug: 4.3.7 - ws: 8.17.1(bufferutil@4.0.9) + ws: 8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-client@4.8.1(bufferutil@4.0.9): + socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.3.7 - engine.io-client: 6.6.3(bufferutil@4.0.9) + engine.io-client: 6.6.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -17521,14 +18113,14 @@ snapshots: transitivePeerDependencies: - supports-color - socket.io@4.8.1(bufferutil@4.0.9): + socket.io@4.8.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): dependencies: accepts: 1.3.8 base64id: 2.0.0 cors: 2.8.5 debug: 4.3.7 - engine.io: 6.6.4(bufferutil@4.0.9) - socket.io-adapter: 2.5.5(bufferutil@4.0.9) + engine.io: 6.6.4(bufferutil@4.0.9)(utf-8-validate@6.0.5) + socket.io-adapter: 2.5.5(bufferutil@4.0.9)(utf-8-validate@6.0.5) socket.io-parser: 4.2.4 transitivePeerDependencies: - bufferutil @@ -17551,7 +18143,7 @@ snapshots: socks@2.8.7: dependencies: - ip-address: 10.0.1 + ip-address: 10.1.0 smart-buffer: 4.2.0 sonic-boom@3.8.1: @@ -17564,11 +18156,11 @@ snapshots: source-map-js@1.2.1: {} - source-map-loader@5.0.0(webpack@5.102.1(esbuild@0.25.11)): + source-map-loader@5.0.0(webpack@5.102.1(esbuild@0.26.0)): dependencies: iconv-lite: 0.6.3 source-map-js: 1.2.1 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) source-map-support@0.4.18: dependencies: @@ -17655,6 +18247,10 @@ snapshots: dependencies: minipass: 7.1.2 + ssri@13.0.0: + dependencies: + minipass: 7.1.2 + stack-trace@0.0.10: {} stackback@0.0.2: {} @@ -17819,7 +18415,7 @@ snapshots: pump: 3.0.3 tar-stream: 3.1.7 optionalDependencies: - bare-fs: 4.4.11 + bare-fs: 4.5.1 bare-path: 3.0.0 transitivePeerDependencies: - bare-abort-controller @@ -17843,7 +18439,7 @@ snapshots: - bare-abort-controller - react-native-b4a - tar@7.5.1: + tar@7.5.2: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -17860,16 +18456,16 @@ snapshots: transitivePeerDependencies: - supports-color - terser-webpack-plugin@5.3.14(esbuild@0.25.11)(webpack@5.102.1(esbuild@0.25.11)): + terser-webpack-plugin@5.3.14(esbuild@0.26.0)(webpack@5.102.1(esbuild@0.26.0)): dependencies: '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 schema-utils: 4.3.3 serialize-javascript: 6.0.2 terser: 5.44.0 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) optionalDependencies: - esbuild: 0.25.11 + esbuild: 0.26.0 terser@5.44.0: dependencies: @@ -17920,15 +18516,15 @@ snapshots: tldts-core@6.1.86: {} - tldts-core@7.0.17: {} + tldts-core@7.0.19: {} tldts@6.1.86: dependencies: tldts-core: 6.1.86 - tldts@7.0.17: + tldts@7.0.19: dependencies: - tldts-core: 7.0.17 + tldts-core: 7.0.19 tmp@0.0.30: dependencies: @@ -17955,7 +18551,7 @@ snapshots: tough-cookie@6.0.0: dependencies: - tldts: 7.0.17 + tldts: 7.0.19 tr46@0.0.3: {} @@ -17977,14 +18573,14 @@ snapshots: dependencies: typescript: 5.9.3 - ts-node@10.9.2(@types/node@22.18.11)(typescript@5.9.3): + ts-node@10.9.2(@types/node@22.19.1)(typescript@5.9.3): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 + '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.18.11 + '@types/node': 22.19.1 acorn: 8.15.0 acorn-walk: 8.3.4 arg: 4.1.3 @@ -18008,8 +18604,8 @@ snapshots: tsx@4.20.6: dependencies: - esbuild: 0.25.11 - get-tsconfig: 4.12.0 + esbuild: 0.25.12 + get-tsconfig: 4.13.0 optionalDependencies: fsevents: 2.3.3 @@ -18017,7 +18613,7 @@ snapshots: dependencies: '@tufjs/models': 4.0.0 debug: 4.4.3(supports-color@10.2.2) - make-fetch-happen: 15.0.2 + make-fetch-happen: 15.0.3 transitivePeerDependencies: - supports-color @@ -18046,7 +18642,7 @@ snapshots: dependencies: content-type: 1.0.5 media-typer: 1.1.0 - mime-types: 3.0.1 + mime-types: 3.0.2 typed-array-buffer@1.0.3: dependencies: @@ -18112,8 +18708,6 @@ snapshots: buffer: 5.7.1 through: 2.3.8 - undici-types@6.21.0: {} - undici-types@7.16.0: {} undici@5.29.0: @@ -18151,11 +18745,11 @@ snapshots: pako: 0.2.9 tiny-inflate: 1.0.3 - unique-filename@4.0.0: + unique-filename@5.0.0: dependencies: - unique-slug: 5.0.0 + unique-slug: 6.0.0 - unique-slug@5.0.0: + unique-slug@6.0.0: dependencies: imurmurhash: 0.1.4 @@ -18169,9 +18763,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.26.3): + update-browserslist-db@1.1.4(browserslist@4.28.0): dependencies: - browserslist: 4.26.3 + browserslist: 4.28.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -18286,16 +18880,16 @@ snapshots: core-util-is: 1.0.2 extsprintf: 1.3.0 - vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 rollup: 4.52.5 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 24.9.1 + '@types/node': 24.10.1 fsevents: 2.3.3 jiti: 2.6.1 less: 4.4.2 @@ -18304,19 +18898,19 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 - vitest@4.0.0(@types/node@24.9.1)(jiti@2.6.1)(jsdom@27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + vitest@4.0.8(@types/node@24.10.1)(jiti@2.6.1)(jsdom@27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5))(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: - '@vitest/expect': 4.0.0 - '@vitest/mocker': 4.0.0(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) - '@vitest/pretty-format': 4.0.0 - '@vitest/runner': 4.0.0 - '@vitest/snapshot': 4.0.0 - '@vitest/spy': 4.0.0 - '@vitest/utils': 4.0.0 + '@vitest/expect': 4.0.8 + '@vitest/mocker': 4.0.8(vite@7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + '@vitest/pretty-format': 4.0.8 + '@vitest/runner': 4.0.8 + '@vitest/snapshot': 4.0.8 + '@vitest/spy': 4.0.8 + '@vitest/utils': 4.0.8 debug: 4.4.3(supports-color@10.2.2) es-module-lexer: 1.7.0 expect-type: 1.2.2 - magic-string: 0.30.19 + magic-string: 0.30.21 pathe: 2.0.3 picomatch: 4.0.3 std-env: 3.10.0 @@ -18324,11 +18918,11 @@ snapshots: tinyexec: 0.3.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vite: 7.2.2(@types/node@24.10.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.9.1 - jsdom: 27.0.1(bufferutil@4.0.9)(postcss@8.5.6)(utf-8-validate@6.0.5) + '@types/node': 24.10.1 + jsdom: 27.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) transitivePeerDependencies: - jiti - less @@ -18365,7 +18959,7 @@ snapshots: web-vitals@4.2.4: {} - webdriver-bidi-protocol@0.3.7: {} + webdriver-bidi-protocol@0.3.9: {} webdriver-js-extender@2.1.0: dependencies: @@ -18392,25 +18986,25 @@ snapshots: webidl-conversions@8.0.0: {} - webpack-dev-middleware@7.4.5(webpack@5.102.1(esbuild@0.25.11)): + webpack-dev-middleware@7.4.5(webpack@5.102.1(esbuild@0.26.0)): dependencies: colorette: 2.0.20 - memfs: 4.49.0 - mime-types: 3.0.1 + memfs: 4.51.0 + mime-types: 3.0.2 on-finished: 2.4.1 range-parser: 1.2.1 schema-utils: 4.3.3 optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) - webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.25.11)): + webpack-dev-server@5.2.2(bufferutil@4.0.9)(utf-8-validate@6.0.5)(webpack@5.102.1(esbuild@0.26.0)): dependencies: '@types/bonjour': 3.5.13 '@types/connect-history-api-fallback': 1.5.4 - '@types/express': 4.17.23 + '@types/express': 4.17.25 '@types/express-serve-static-core': 4.19.7 '@types/serve-index': 1.9.4 - '@types/serve-static': 1.15.9 + '@types/serve-static': 1.15.10 '@types/sockjs': 0.3.36 '@types/ws': 8.18.1 ansi-html-community: 0.0.8 @@ -18421,9 +19015,9 @@ snapshots: connect-history-api-fallback: 2.0.0 express: 4.21.2 graceful-fs: 4.2.11 - http-proxy-middleware: 2.0.9(@types/express@4.17.23) + http-proxy-middleware: 2.0.9(@types/express@4.17.25) ipaddr.js: 2.2.0 - launch-editor: 2.11.1 + launch-editor: 2.12.0 open: 10.2.0 p-retry: 6.2.1 schema-utils: 4.3.3 @@ -18431,10 +19025,10 @@ snapshots: serve-index: 1.9.1 sockjs: 0.3.24 spdy: 4.0.2 - webpack-dev-middleware: 7.4.5(webpack@5.102.1(esbuild@0.25.11)) + webpack-dev-middleware: 7.4.5(webpack@5.102.1(esbuild@0.26.0)) ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5) optionalDependencies: - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) transitivePeerDependencies: - bufferutil - debug @@ -18449,12 +19043,12 @@ snapshots: webpack-sources@3.3.3: {} - webpack-subresource-integrity@5.1.0(webpack@5.102.1(esbuild@0.25.11)): + webpack-subresource-integrity@5.1.0(webpack@5.102.1(esbuild@0.26.0)): dependencies: typed-assert: 1.0.9 - webpack: 5.102.1(esbuild@0.25.11) + webpack: 5.102.1(esbuild@0.26.0) - webpack@5.102.1(esbuild@0.25.11): + webpack@5.102.1(esbuild@0.26.0): dependencies: '@types/eslint-scope': 3.7.7 '@types/estree': 1.0.8 @@ -18464,7 +19058,7 @@ snapshots: '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 acorn-import-phases: 1.0.4(acorn@8.15.0) - browserslist: 4.26.3 + browserslist: 4.28.0 chrome-trace-event: 1.0.4 enhanced-resolve: 5.18.3 es-module-lexer: 1.7.0 @@ -18478,7 +19072,7 @@ snapshots: neo-async: 2.6.2 schema-utils: 4.3.3 tapable: 2.3.0 - terser-webpack-plugin: 5.3.14(esbuild@0.25.11)(webpack@5.102.1(esbuild@0.25.11)) + terser-webpack-plugin: 5.3.14(esbuild@0.26.0)(webpack@5.102.1(esbuild@0.26.0)) watchpack: 2.4.4 webpack-sources: 3.3.3 transitivePeerDependencies: @@ -18570,6 +19164,10 @@ snapshots: dependencies: isexe: 3.1.1 + which@6.0.0: + dependencies: + isexe: 3.1.1 + why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 @@ -18613,9 +19211,10 @@ snapshots: optionalDependencies: bufferutil: 4.0.9 - ws@8.17.1(bufferutil@4.0.9): + ws@8.17.1(bufferutil@4.0.9)(utf-8-validate@6.0.5): optionalDependencies: bufferutil: 4.0.9 + utf-8-validate: 6.0.5 ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@6.0.5): optionalDependencies: @@ -18636,7 +19235,7 @@ snapshots: xml2js@0.4.23: dependencies: - sax: 1.4.1 + sax: 1.4.3 xmlbuilder: 11.0.1 xmlbuilder@11.0.1: {} @@ -18728,7 +19327,7 @@ snapshots: yoctocolors@2.1.2: {} - zod-to-json-schema@3.24.6(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@3.25.76): dependencies: zod: 3.25.76 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 6c4a8f21d754..d1752e85bda6 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -26,5 +26,3 @@ minimumReleaseAgeExclude: - '@ngtools/webpack' - '@schematics/*' - 'ng-packagr' - - 'vitest' # temporary to support v21 - - '@vitest/*' # temporary to support v21 diff --git a/scripts/build-schema.mts b/scripts/build-schema.mts deleted file mode 100644 index ffc042af9630..000000000000 --- a/scripts/build-schema.mts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @license - * Copyright Google LLC All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.dev/license - */ - -import { spawn } from 'node:child_process'; -import { rm } from 'node:fs/promises'; -import { join, resolve } from 'node:path'; - -const __dirname = import.meta.dirname; -const baseDir = resolve(`${__dirname}/..`); -const bazelCmd = process.env.BAZEL ?? `pnpm -s bazel`; -const distRoot = join(baseDir, '/dist-schema/'); - -function _clean() { - console.info('Cleaning...'); - console.info(' Removing dist-schema/...'); - - return rm(join(__dirname, '../dist-schema'), { force: true, recursive: true, maxRetries: 3 }); -} - -function _exec(cmd: string, captureStdout: boolean): Promise { - return new Promise((resolve, reject) => { - const proc = spawn(cmd, { - stdio: 'pipe', - shell: true, - env: { - HOME: process.env.HOME, - PATH: process.env.PATH, - }, - }); - - let output = ''; - proc.stdout.on('data', (data) => { - console.info(data.toString().trim()); - if (captureStdout) { - output += data.toString(); - } - }); - proc.stderr.on('data', (data) => console.info(data.toString().trim())); - - proc.on('error', (error) => { - console.error(error.message); - }); - - proc.on('exit', (status) => { - if (status !== 0) { - reject(`Command failed: ${cmd}`); - } else { - resolve(output); - } - }); - }); -} - -async function _buildSchemas(): Promise { - console.info(`Building schemas...`); - - const queryTargetsCmd = `${bazelCmd} query --output=label "attr(name, .*_schema, //packages/...)"`; - const targets = (await _exec(queryTargetsCmd, true)).split(/\r?\n/); - - await _exec(`${bazelCmd} build ${targets.join(' ')} --symlink_prefix=${distRoot}`, false); -} - -export default async function (argv: {}): Promise { - await _clean(); - - await _buildSchemas(); -} diff --git a/tests/legacy-cli/BUILD.bazel b/tests/legacy-cli/BUILD.bazel index 0b66850c52b2..713bc31b5c82 100644 --- a/tests/legacy-cli/BUILD.bazel +++ b/tests/legacy-cli/BUILD.bazel @@ -68,6 +68,7 @@ e2e_suites( # Extra runtime deps due to bundling issues. # TODO: Clean this up. "//:node_modules/express", + "//:node_modules/undici", ], runner = ":runner_entrypoint", ) diff --git a/tests/legacy-cli/e2e.bzl b/tests/legacy-cli/e2e.bzl index fe7af0c1d133..62a89ee6a76f 100644 --- a/tests/legacy-cli/e2e.bzl +++ b/tests/legacy-cli/e2e.bzl @@ -56,6 +56,7 @@ WEBPACK_IGNORE_TESTS = [ "tests/build/auto-csp*", "tests/build/incremental-watch.js", "tests/build/chunk-optimizer.js", + "tests/build/chunk-optimizer-lazy.js", ] def _to_glob(patterns): diff --git a/tests/legacy-cli/e2e/assets/BUILD.bazel b/tests/legacy-cli/e2e/assets/BUILD.bazel index 946db62d0d5a..11bc738d4a29 100644 --- a/tests/legacy-cli/e2e/assets/BUILD.bazel +++ b/tests/legacy-cli/e2e/assets/BUILD.bazel @@ -2,6 +2,9 @@ load("//tools:defaults.bzl", "copy_to_bin") copy_to_bin( name = "assets", - srcs = glob(["**"]), + srcs = glob( + include = ["**"], + exclude = ["BUILD.bazel"], + ), visibility = ["//visibility:public"], ) diff --git a/tests/legacy-cli/e2e/assets/add-collection/collection.json b/tests/legacy-cli/e2e/assets/add-collection-dir/collection.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection/collection.json rename to tests/legacy-cli/e2e/assets/add-collection-dir/collection.json diff --git a/tests/legacy-cli/e2e/assets/add-collection/index.js b/tests/legacy-cli/e2e/assets/add-collection-dir/index.js similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection/index.js rename to tests/legacy-cli/e2e/assets/add-collection-dir/index.js diff --git a/tests/legacy-cli/e2e/assets/add-collection/package.json b/tests/legacy-cli/e2e/assets/add-collection-dir/package.json similarity index 100% rename from tests/legacy-cli/e2e/assets/add-collection/package.json rename to tests/legacy-cli/e2e/assets/add-collection-dir/package.json diff --git a/tests/legacy-cli/e2e/initialize/500-create-project.ts b/tests/legacy-cli/e2e/initialize/500-create-project.ts index c6c0f8ef243d..b48c8733a9a5 100644 --- a/tests/legacy-cli/e2e/initialize/500-create-project.ts +++ b/tests/legacy-cli/e2e/initialize/500-create-project.ts @@ -64,6 +64,9 @@ export default async function () { const test = json['projects']['test-project']['architect']['test']; test.builder = '@angular-devkit/build-angular:karma'; + test.options ??= {}; + test.options.tsConfig = 'tsconfig.spec.json'; + delete test.options.runner; }); await updateJsonFile('tsconfig.json', (tsconfig) => { delete tsconfig.compilerOptions.esModuleInterop; diff --git a/tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts b/tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts deleted file mode 100644 index 4914921eca18..000000000000 --- a/tests/legacy-cli/e2e/setup/001-create-tmp-dir.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { dirname } from 'node:path'; -import { getGlobalVariable, setGlobalVariable } from '../utils/env'; -import { mktempd } from '../utils/utils'; - -export default async function () { - const argv = getGlobalVariable('argv'); - - // Get to a temporary directory. - let tempRoot: string; - if (argv.reuse) { - tempRoot = dirname(argv.reuse); - } else if (argv.tmpdir) { - tempRoot = argv.tmpdir; - } else { - tempRoot = await mktempd('angular-cli-e2e-', process.env.E2E_TEMP); - } - console.log(` Using "${tempRoot}" as temporary directory for a new project.`); - setGlobalVariable('tmp-root', tempRoot); -} diff --git a/tests/legacy-cli/e2e/setup/002-npm-sandbox.ts b/tests/legacy-cli/e2e/setup/001-npm-sandbox.ts similarity index 100% rename from tests/legacy-cli/e2e/setup/002-npm-sandbox.ts rename to tests/legacy-cli/e2e/setup/001-npm-sandbox.ts diff --git a/tests/legacy-cli/e2e/setup/100-global-cli.ts b/tests/legacy-cli/e2e/setup/100-global-cli.ts index 780c0bbfaf39..9f587fa5c38d 100644 --- a/tests/legacy-cli/e2e/setup/100-global-cli.ts +++ b/tests/legacy-cli/e2e/setup/100-global-cli.ts @@ -6,7 +6,7 @@ const PACKAGE_MANAGER_VERSION = { 'npm': '10.8.1', 'yarn': '1.22.22', 'pnpm': '10.17.1', - 'bun': '1.2.21', + 'bun': '1.3.2', }; export default async function () { diff --git a/tests/legacy-cli/e2e/tests/BUILD.bazel b/tests/legacy-cli/e2e/tests/BUILD.bazel index 55f5019b568f..0ed3f83428f7 100644 --- a/tests/legacy-cli/e2e/tests/BUILD.bazel +++ b/tests/legacy-cli/e2e/tests/BUILD.bazel @@ -13,6 +13,7 @@ ts_project( "//:node_modules/express", "//:node_modules/fast-glob", "//:node_modules/semver", + "//:node_modules/undici", "//tests/legacy-cli/e2e/utils", ], ) diff --git a/tests/legacy-cli/e2e/tests/basic/test.ts b/tests/legacy-cli/e2e/tests/basic/test.ts index d9066946ae8e..50580581d442 100644 --- a/tests/legacy-cli/e2e/tests/basic/test.ts +++ b/tests/legacy-cli/e2e/tests/basic/test.ts @@ -1,5 +1,6 @@ import { ng } from '../../utils/process'; import { writeMultipleFiles } from '../../utils/fs'; +import { getGlobalVariable } from '../../utils/env'; export default async function () { // make sure both --watch=false work @@ -48,5 +49,11 @@ export default async function () { `, }); - await ng('test', '--watch=false', '--karma-config=karma.conf.bis.js'); + const isWebpack = !getGlobalVariable('argv')['esbuild']; + + if (isWebpack) { + await ng('test', '--watch=false', '--karma-config=karma.conf.bis.js'); + } else { + await ng('test', '--watch=false', '--runner-config=karma.conf.bis.js'); + } } diff --git a/tests/legacy-cli/e2e/tests/build/chunk-optimizer-lazy.ts b/tests/legacy-cli/e2e/tests/build/chunk-optimizer-lazy.ts new file mode 100644 index 000000000000..7f57e6d88e68 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/build/chunk-optimizer-lazy.ts @@ -0,0 +1,52 @@ +import assert from 'node:assert/strict'; +import { readdir } from 'node:fs/promises'; +import { replaceInFile } from '../../utils/fs'; +import { execWithEnv, ng } from '../../utils/process'; + +export default async function () { + // Add lazy routes. + await ng('generate', 'component', 'lazy-a'); + await ng('generate', 'component', 'lazy-b'); + await ng('generate', 'component', 'lazy-c'); + await replaceInFile( + 'src/app/app.routes.ts', + 'routes: Routes = [];', + `routes: Routes = [ + { + path: 'lazy-a', + loadComponent: () => import('./lazy-a/lazy-a').then(m => m.LazyA), + }, + { + path: 'lazy-b', + loadComponent: () => import('./lazy-b/lazy-b').then(m => m.LazyB), + }, + { + path: 'lazy-c', + loadComponent: () => import('./lazy-c/lazy-c').then(m => m.LazyC), + }, + ];`, + ); + + // Build without chunk optimization + await ng('build', '--output-hashing=none'); + const unoptimizedFiles = await readdir('dist/test-project/browser'); + const unoptimizedJsFiles = unoptimizedFiles.filter((f) => f.endsWith('.js')); + + // Build with chunk optimization + await execWithEnv('ng', ['build', '--output-hashing=none'], { + ...process.env, + NG_BUILD_OPTIMIZE_CHUNKS: '1', + }); + const optimizedFiles = await readdir('dist/test-project/browser'); + const optimizedJsFiles = optimizedFiles.filter((f) => f.endsWith('.js')); + + // Check that the number of chunks is reduced but not all combined + assert.ok( + optimizedJsFiles.length < unoptimizedJsFiles.length, + `Expected chunk count to be less than ${unoptimizedJsFiles.length}, but was ${optimizedJsFiles.length}.`, + ); + assert.ok( + optimizedJsFiles.length > 1, + `Expected more than one chunk, but found ${optimizedJsFiles.length}.`, + ); +} diff --git a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts index 617776a94dc7..77f954be4f4d 100644 --- a/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts +++ b/tests/legacy-cli/e2e/tests/build/server-rendering/server-routes-output-mode-static.ts @@ -29,7 +29,8 @@ export default async function () { await writeFile( 'src/app/app.routes.ts', ` - import { Routes } from '@angular/router'; + import { inject } from '@angular/core'; + import { Routes, Router } from '@angular/router'; import { Home } from './home/home'; import { Ssg } from './ssg/ssg'; import { SsgWithParams } from './ssg-with-params/ssg-with-params'; @@ -47,6 +48,12 @@ export default async function () { path: 'ssg-redirect', redirectTo: 'ssg' }, + { + path: 'ssg-redirect-via-guard', + canActivate: [() => { + return inject(Router).createUrlTree(['ssg'], { queryParams: { foo: 'bar' }}) + }], + }, { path: 'ssg/:id', component: SsgWithParams, @@ -106,8 +113,10 @@ export default async function () { 'ssg/index.html': /ng-server-context="ssg".+ssg works!/, 'ssg/one/index.html': /ng-server-context="ssg".+ssg-with-params works!/, 'ssg/two/index.html': /ng-server-context="ssg".+ssg-with-params works!/, - // When static redirects as generated as meta tags. + // When static redirects are generated as meta tags. 'ssg-redirect/index.html': '', + 'ssg-redirect-via-guard/index.html': + '', }; for (const [filePath, fileMatch] of Object.entries(expects)) { diff --git a/tests/legacy-cli/e2e/tests/commands/add/base.ts b/tests/legacy-cli/e2e/tests/commands/add/base.ts index f4e7048df6ac..d31210c6c242 100644 --- a/tests/legacy-cli/e2e/tests/commands/add/base.ts +++ b/tests/legacy-cli/e2e/tests/commands/add/base.ts @@ -4,7 +4,7 @@ import { ng } from '../../../utils/process'; import { expectToFail } from '../../../utils/utils'; export default async function () { - await symlinkFile(assetDir('add-collection'), `./node_modules/add-collection`, 'dir'); + await symlinkFile(assetDir('add-collection-dir'), `./node_modules/add-collection`, 'dir'); await ng('add', 'add-collection'); await expectFileToExist('empty-file'); diff --git a/tests/legacy-cli/e2e/tests/commands/add/dir.ts b/tests/legacy-cli/e2e/tests/commands/add/dir.ts index f5fadc486b3d..7cb00704cc8e 100644 --- a/tests/legacy-cli/e2e/tests/commands/add/dir.ts +++ b/tests/legacy-cli/e2e/tests/commands/add/dir.ts @@ -1,8 +1,19 @@ +import { cp } from 'node:fs/promises'; +import { resolve } from 'node:path'; import { assetDir } from '../../../utils/assets'; import { expectFileToExist } from '../../../utils/fs'; import { ng } from '../../../utils/process'; export default async function () { - await ng('add', assetDir('add-collection'), '--name=blah', '--skip-confirmation'); + const collectionName = 'add-collection-dir'; + const dirCollectionPath = resolve(collectionName); + + // Copy locally as bun doesn't install the dependency correctly if it has symlinks. + await cp(assetDir(collectionName), dirCollectionPath, { + recursive: true, + dereference: true, + }); + + await ng('add', dirCollectionPath, '--name=blah', '--skip-confirmation'); await expectFileToExist('blah'); } diff --git a/tests/legacy-cli/e2e/tests/commands/add/peer.ts b/tests/legacy-cli/e2e/tests/commands/add/peer.ts index d085efadf080..143542e4533c 100644 --- a/tests/legacy-cli/e2e/tests/commands/add/peer.ts +++ b/tests/legacy-cli/e2e/tests/commands/add/peer.ts @@ -1,24 +1,44 @@ import assert from 'node:assert/strict'; +import { resolve } from 'node:path'; +import { cp } from 'node:fs/promises'; import { assetDir } from '../../../utils/assets'; import { ng } from '../../../utils/process'; -const warning = 'Adding the package may not succeed.'; +export default async function (): Promise { + const warning = /Adding the package may not succeed/; -export default async function () { - const { stdout: bad } = await ng( - 'add', - assetDir('add-collection-peer-bad'), - '--skip-confirmation', + const stdout1 = await runNgAdd('add-collection-peer-bad'); + assert.match( + stdout1, + warning, + `Peer warning should be shown for add-collection-peer-bad but was not.`, ); - assert.match(bad, new RegExp(warning), 'peer warning not shown on bad package'); - const { stdout: base } = await ng('add', assetDir('add-collection'), '--skip-confirmation'); - assert.doesNotMatch(base, new RegExp(warning), 'peer warning shown on base package'); + const stdout2 = await runNgAdd('add-collection-dir'); + assert.doesNotMatch( + stdout2, + warning, + `Peer warning should NOT be shown for add-collection-dir but was.`, + ); - const { stdout: good } = await ng( - 'add', - assetDir('add-collection-peer-good'), - '--skip-confirmation', + const stdout3 = await runNgAdd('add-collection-peer-good'); + assert.doesNotMatch( + stdout3, + warning, + `Peer warning should NOT be shown for add-collection-peer-good but was.`, ); - assert.doesNotMatch(good, new RegExp(warning), 'peer warning shown on good package'); +} + +async function runNgAdd(collectionName: string): Promise { + const collectionPath = resolve(collectionName); + + // Copy locally as bun doesn't install the dependency correctly if it has symlinks. + await cp(assetDir(collectionName), collectionPath, { + recursive: true, + dereference: true, + }); + + const { stdout } = await ng('add', collectionPath, '--skip-confirmation'); + + return stdout; } diff --git a/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts b/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts index a27ba708637d..95218fd653d9 100644 --- a/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts +++ b/tests/legacy-cli/e2e/tests/commands/add/secure-registry.ts @@ -1,42 +1,49 @@ -import { expectFileNotToExist, expectFileToExist } from '../../../utils/fs'; +import { expectFileNotToExist, expectFileToExist, rimraf } from '../../../utils/fs'; import { getActivePackageManager, installWorkspacePackages } from '../../../utils/packages'; import { git, ng } from '../../../utils/process'; import { createNpmConfigForAuthentication } from '../../../utils/registry'; import { expectToFail } from '../../../utils/utils'; export default async function () { - // The environment variable has priority over the .npmrc - delete process.env['NPM_CONFIG_REGISTRY']; - const isNpm = getActivePackageManager() === 'npm'; + const originalNpmConfigRegistry = process.env['NPM_CONFIG_REGISTRY']; + try { + // The environment variable has priority over the .npmrc + delete process.env['NPM_CONFIG_REGISTRY']; + const packageManager = getActivePackageManager(); + const supportsUnscopedAuth = packageManager !== 'bun' && packageManager !== 'npm'; + const command = ['add', '@angular/pwa', '--skip-confirmation']; - const command = ['add', '@angular/pwa', '--skip-confirmation']; - await expectFileNotToExist('public/manifest.webmanifest'); + await expectFileNotToExist('public/manifest.webmanifest'); - // Works with unscoped registry authentication details - if (!isNpm) { - // NPM no longer support unscoped. - await createNpmConfigForAuthentication(false); + // Works with unscoped registry authentication details + if (supportsUnscopedAuth) { + // Some package managers such as Bun and NPM do not support unscoped auth. + await createNpmConfigForAuthentication(false); + await ng(...command); + await expectFileToExist('public/manifest.webmanifest'); + await git('clean', '-dxf'); + } + + // Works with scoped registry authentication details + await expectFileNotToExist('public/manifest.webmanifest'); + + await createNpmConfigForAuthentication(true); await ng(...command); await expectFileToExist('public/manifest.webmanifest'); await git('clean', '-dxf'); - } - // Works with scoped registry authentication details - await expectFileNotToExist('public/manifest.webmanifest'); - await createNpmConfigForAuthentication(true); - await ng(...command); - await expectFileToExist('public/manifest.webmanifest'); + // Invalid authentication token + if (!supportsUnscopedAuth) { + // Some package managers such as Bun and NPM do not support unscoped auth. + await createNpmConfigForAuthentication(false, true); + await expectToFail(() => ng(...command)); + } - // Invalid authentication token - if (isNpm) { - // NPM no longer support unscoped. - await createNpmConfigForAuthentication(false, true); + await createNpmConfigForAuthentication(true, true); await expectToFail(() => ng(...command)); + } finally { + process.env['NPM_CONFIG_REGISTRY'] = originalNpmConfigRegistry; + await git('clean', '-dxf'); + await installWorkspacePackages(); } - - await createNpmConfigForAuthentication(true, true); - await expectToFail(() => ng(...command)); - - await git('clean', '-dxf'); - await installWorkspacePackages(); } diff --git a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts index aa7e33089666..19f1208646d6 100644 --- a/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts +++ b/tests/legacy-cli/e2e/tests/commands/serve/ssr-http-requests-assets.ts @@ -1,5 +1,5 @@ import assert from 'node:assert'; - +import { Agent } from 'undici'; import { killAllProcesses, ng } from '../../../utils/process'; import { writeMultipleFiles } from '../../../utils/fs'; import { installWorkspacePackages, uninstallPackage } from '../../../utils/packages'; @@ -71,11 +71,120 @@ export default async function () { await killAllProcesses(); - try { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - const sslPort = await ngServe('--ssl'); - assert.match(await (await fetch(`https://localhost:${sslPort}/`)).text(), match); - } finally { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; - } + const sslPort = await ngServe('--ssl'); + assert.match( + await ( + await fetch(`https://localhost:${sslPort}/`, { + dispatcher: new Agent({ + connect: { + rejectUnauthorized: false, + }, + }), + }) + ).text(), + match, + ); + + await killAllProcesses(); + + // With OpenSSl cert+key + writeMultipleFiles({ + 'server.key': `-----BEGIN PRIVATE KEY----- +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDOyBmVy61zEqfs +oTPQ9gTX233/nlrVXtaUGJkbDR5actq0X+XQtZuIoO4JgRpiYz5/8XiY8AiaMdt3 +0abugO5AhyIbsGyQxvz2si7yKQ+WUdF/DRTpfTq76E8EXR8W9BI+DTpG/1nNGBd1 +lpMa8NMfHqLvhtpebHuBcb1BCRely+FILHVDGf+dfCIvR0Zvt8Ah3qLL6vvX3pzF +qM9XrXfUgWKqpbz+L1BeCPILsH+UaOOCzzbyrLoY6fnawjUkc/ieHWycro7dBUlu +JJ/kcGtpPYD/hsMFcXz6D67qbIFuc6Dz9CrWIagAFMqK91FAtUpyfP7+jLikQOST +pFDLgJ+klADGCiZ6C/dvjUsYM+4ML9dX6Q9rj6sQo/oj0Dsdj39J5mVmklkbRP5q +heMGTyc09/ambiYFfzWEMMnEcCT/CS/r1q93oRDG02Cx6F1B05mtR+/AFxAJ6UxL +2u3oMPVY179FWjx2+YvbfrdNrFnWb8eRMiRZIW8O8ptKkrh+LL1Rhep+W/1Nxdrp +g7E2rWP8AWr3mdd+cnauvF/2yMecBDLVnk3OOSjcuLc+i9ngOD0xHdcRfO89mryj +IewEIrUQ4U0ZgyHMi99qV4wyXhd9HzTUgT01QofsiuF9xyVfnansQOj3oqOgCS92 +VEeqZnLXgaVoh/++/FV7r4C5zxLzLwIDAQABAoICAAeKSqD98iE3o5qc6AAiqj79 +r8L2dJ+0F9cDF4Bh6aLFYBGUoS/Sr38Cm7m0/3qiiEKvbpM9/0QVfHLRoBNcJnBk +0mrp1yD1tfEOUPcJ12D/3XJ2zlIv+7oUn97Ia9h4NCzBv5zw7lTsrjHenDMSZ7XD +PR6qb064XfiRETKFeCJk64Godj/3QkmX2FApCMDwXJttynLQseK5RZnDHojhuDuR +vgfC+aOCTit8GOkxi1Hdppxm8tmMwfqyJmAJh5IdKkNA3MHtbyPCxSXRRIUdwMXT +bhhVCh9/W3prv/vEYSPfRGs9WdtrTBj/U8GlgGlxa87h1i/i8N4I5RP+8lic6zVL +BIIPamkRFRNUmV7ZzpWsrLl1TUUcQJ1UsjNqaLD7jl+l0IaUta8I9crJQWIuQu+G +5C0XJQPZrqGkZfLSMvi08S+8myCzf+3P3ayUHAKz4Q1pTeM2BbHQi1HbT+WUsA5G +DD4xBwc8VJXOy0dB4v4e5eK8aZaJZroR5LJT7bvKw1MNpyAt6w2Z17eSSuEE0x6u +4uzOfHRaWiKH9gXVSKyo8xM08wiKAJIpDg4fDsu/XPjfPzV3eSHwin6ADw7rcOrW +j4Ca43Ts7Fz0Y40dtUyrrQ3f7WSQ9C+M88NuI9WYPWmXqPQY9+b5Au0Q8rq1j3dW +1YB3vYd6ElaLI6k7c5OhAoIBAQDt+Dgi2jrx2Xol0Per/cIFyG/hX/h+tavj++xl +gIMLLwhFmBVIkkXHjG5v5rZFCY7giQgdy+JHAIDUg3Ae3K7zSYidkMwQzLJ9udaT +nJEybY4RlEJZVBs58pkjevqTD/pZ+Kj09/VLAJIhOInFQHQ+ZVn4uHF+NO4tcsH+ +Wtsyyf8tFMkoNQ38o2oTnJtsotssKGdXCgi36BCCCUQk98113RK9dBTi+2iB59qr +WczAb6Jl5cs1j/2IC3z9KilZ3/ww4Bshs5LThIGR66KZIfApzf8XQzHM9mhiLgRU +thUZ0a/ougqf4FovLAezsNM7kYqbPDPOh/CayN5KZ8pHNLt1AoIBAQDecvFejv3u +Lm9kf2xRv06HTsLeEUSgRVoWdKidwW3tXOkl8vuBTzeFl9yrgtMBbSgcFASbEKPP +uPc6g+zkcakUB+FLGGNwNFKhdGPUMI7u8i9WeWH+e3Aios7n0tCPP0Xv6d1Lhcyw +X1nz07hZ+sT40nLGyf/6vfg8LFGSBrr3YQLseodKGTC9jc5yJqEX16cqHppkwaJT +Elsona7PZGFm/WFGWn4wZiPpd9P5lnxP+KrI+m84z4Gw5txcJsE8WiUrrQYHG3+2 +yeztwYl+JGHcspsU4WTPCupyVRHt0uuGVN+UhLKgER8wghc6fL08jGkHgVLrStnN +ekRA0gEZRzOTAoIBAGuQMheW2uPssGidfwXP6r5gbinKDnF/vpWLjrwGjbUlajDC +4IPwEfhzwot0Flk4S8u0ROXq/XmogZMNYkWg7LdtOoI2K/c//0ITGSmZsIvBt2C8 +ygzElpXn0U6XTOHia//1BLHNzqM7O9ImUyfEzYZSm4twG2S3mh0S7RsCiGf5pA0F +gzNYX90dJFp/BEXjivv3u1Y9Y9l03NlaROIM3GL1LX5TFQnQJ9noKhAfxAwLqbUz +XFn2ntu6jaGFSDGmq8CP29Os7qYLE+IYR2O+UmcjBLXIGp+RlXcjY7PCpeEIxeGF +Dj5b04fU+BpByAj57VPjr2sgSSI9vzSUm3r6G+0CggEBALK7JgZ028BxHN1hqHWy +QXVkKhxlQX+I2Y5rY0OFtD5gRZBRQBUwwgqb7xj7P3DI9M5Co0S4RPZUxogEkeUn +EdPfVPySdusjjzTcoI1QCrggbTqMwtjG811Q9O+9Kge+rgHLJRxWQBWCN3M6rMfX +PkYySThB+2PLGVW3wj6TG8xB7Sh2dpdp0AitlK+RLCRNCKpF9oV4M2WNvSLQNzG5 +lK08btkpQnS+zKH8vpuudumGgiqDVbQOvkSV6X49QUutnmoOVmaFiMMkUTLjKwbo +Up0SAJrxUp8sRR1iDsrIiqbfMNlTGXaU6zt9ew5qRV4N7yGxnh8hgAih8Y8nbOyT +kfMCggEBAMVOGy7yzaMQvVSkcFkUAnznI7RwvhUWusomfyaVgHm+j3Y7OPius1ah +4Da3kvb4t8OFUIdnMz/rte8xRKE6lKLNmXG9+8nPkCdrB9ovDr0Pw3ONZ7kKHuhm +75IKV72f3krZK5jJ88p/ruUUotButZb+WlGW5qQOJEJnHi65ABGYchAADAOBflXK +XbklHb6sVmEx6Ds4OMAbEmgH4C7BZuvmVeYMY7ihGIuBF3rE70rc2meQl/fxn0Gd ++/FrHDqCSkXwNT69HEOoLT/hi6Pc3kyn1bFOK+W8AydilI+6yOKkiYTSoCAO/yi/ +xlFXnn9FIQthAEWUhFgqApO+oKBn0hw= +-----END PRIVATE KEY----- +`, + 'server.crt': `-----BEGIN CERTIFICATE----- +MIIFCTCCAvGgAwIBAgIUd0CiuFYYUTnnfB/Q6lijpEZJy4wwDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MTEwNzEwMTE0NFoXDTI2MTEw +NzEwMTE0NFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEAzsgZlcutcxKn7KEz0PYE19t9/55a1V7WlBiZGw0eWnLa +tF/l0LWbiKDuCYEaYmM+f/F4mPAImjHbd9Gm7oDuQIciG7BskMb89rIu8ikPllHR +fw0U6X06u+hPBF0fFvQSPg06Rv9ZzRgXdZaTGvDTHx6i74baXmx7gXG9QQkXpcvh +SCx1Qxn/nXwiL0dGb7fAId6iy+r7196cxajPV6131IFiqqW8/i9QXgjyC7B/lGjj +gs828qy6GOn52sI1JHP4nh1snK6O3QVJbiSf5HBraT2A/4bDBXF8+g+u6myBbnOg +8/Qq1iGoABTKivdRQLVKcnz+/oy4pEDkk6RQy4CfpJQAxgomegv3b41LGDPuDC/X +V+kPa4+rEKP6I9A7HY9/SeZlZpJZG0T+aoXjBk8nNPf2pm4mBX81hDDJxHAk/wkv +69avd6EQxtNgsehdQdOZrUfvwBcQCelMS9rt6DD1WNe/RVo8dvmL2363TaxZ1m/H +kTIkWSFvDvKbSpK4fiy9UYXqflv9TcXa6YOxNq1j/AFq95nXfnJ2rrxf9sjHnAQy +1Z5Nzjko3Li3PovZ4Dg9MR3XEXzvPZq8oyHsBCK1EOFNGYMhzIvfaleMMl4XfR80 +1IE9NUKH7IrhfcclX52p7EDo96KjoAkvdlRHqmZy14GlaIf/vvxVe6+Auc8S8y8C +AwEAAaNTMFEwHQYDVR0OBBYEFCOiC0xvMbfCFzmseoMDht+ydKBbMB8GA1UdIwQY +MBaAFCOiC0xvMbfCFzmseoMDht+ydKBbMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBAJSiQwcaGVhwUorkb062cyyZOAstEJ5meg6H2g3nL894oWEU +FLc/S20z2tqO1It4rZB3cRKmB0RvH78eh4aUPAh0lPa/bm/h7WrgdEAJUmlNuZV3 +Hitd/c1d2OVzx6w+CFYd/G5GW3sWblYiH0paIN6s4TqHFY/IAzzZKQB7Ud7FJagM +KMkEP8RFDm7iRcENuSf51LtZb2NjN1TM5CK5sVXu62dvPYZC6SW052/qd1U+1Tyw +EX4fCqUgEoGoU6+Ftz3hCdVy3E4uzFBK1e5wmct6HULBZL51PWpf3BgwneZy0itE +lD6Y0H6m/9KMVcXpAHZK+6YnOOcWxIgfjykjZEO99rx3pVWPw1uSBUJEu1SLknAn +JDe+WLp+xmB8s62EjixZsEGqoQYYrtZ3vz8u4PSSgYPJjdAkFdLOPitf0U8ZW9/7 +hGyHgqd7WQ3toBwwdnPo6fZqHHyN8rXeWcmx8Uj9oyY1uunkSmq3csITPQg/zKBO +6RsO3pPj8mHjeAZCDs+Ks68ccPsn+53fJ9CrjiJlHFIP0ywbEBO1snJDit5q3gQI +/UpClB9Sl+mz4wznOvrKycrxrLEptZeBA5c6M9Qr30YJAb/prxvzSY5FrUGcstkO +CQVzSwZEUXxSo6K4cJ55vC0p3P3aoMvEpHfM+JqL3lCM9qWrxfkhvn8YS+Gg +-----END CERTIFICATE----- +`, + }); + + const sslPortForCerts = await ngServe('--ssl', '--ssl-cert=server.crt', '--ssl-key="server.key"'); + assert.match( + await ( + await fetch(`https://localhost:${sslPortForCerts}/`, { + dispatcher: new Agent({ + connect: { + rejectUnauthorized: false, + }, + }), + }) + ).text(), + match, + ); } diff --git a/tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts b/tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts index 3533e6c8e9a9..808bcb59e2d9 100644 --- a/tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts +++ b/tests/legacy-cli/e2e/tests/test/test-code-coverage-exclude.ts @@ -1,10 +1,14 @@ +import { getGlobalVariable } from '../../utils/env'; import { expectFileToExist, rimraf } from '../../utils/fs'; import { silentNg } from '../../utils/process'; import { expectToFail } from '../../utils/utils'; export default async function () { + const isWebpack = !getGlobalVariable('argv')['esbuild']; + const coverageOptionName = isWebpack ? '--code-coverage' : '--coverage'; + // This test is already in build-angular, but that doesn't run on Windows. - await silentNg('test', '--no-watch', '--code-coverage'); + await silentNg('test', '--no-watch', coverageOptionName); await expectFileToExist('coverage/test-project/app.ts.html'); // Delete coverage directory await rimraf('coverage'); @@ -12,8 +16,8 @@ export default async function () { await silentNg( 'test', '--no-watch', - '--code-coverage', - `--code-coverage-exclude='src/**/app.ts'`, + coverageOptionName, + `${coverageOptionName}-exclude='src/**/app.ts'`, ); // Doesn't include excluded. diff --git a/tests/legacy-cli/e2e/tests/test/test-environment.ts b/tests/legacy-cli/e2e/tests/test/test-environment.ts index 1a4dadaa4317..1670cb246521 100644 --- a/tests/legacy-cli/e2e/tests/test/test-environment.ts +++ b/tests/legacy-cli/e2e/tests/test/test-environment.ts @@ -1,21 +1,23 @@ import { ng } from '../../utils/process'; import { writeFile, writeMultipleFiles } from '../../utils/fs'; import { updateJsonFile } from '../../utils/project'; +import { getGlobalVariable } from '../../utils/env'; + +export default async function () { + const isWebpack = !getGlobalVariable('argv')['esbuild']; -export default function () { // Tests run in 'dev' environment by default. - return ( - writeMultipleFiles({ - 'src/environment.prod.ts': ` + await writeMultipleFiles({ + 'src/environment.prod.ts': ` export const environment = { production: true };`, - 'src/environment.ts': ` + 'src/environment.ts': ` export const environment = { production: false }; `, - 'src/app/environment.spec.ts': ` + 'src/app/environment.spec.ts': ` import { environment } from '../environment'; describe('Test environment', () => { @@ -24,29 +26,33 @@ export default function () { }); }); `, - }) - .then(() => ng('test', '--watch=false')) - .then(() => - updateJsonFile('angular.json', (configJson) => { - const appArchitect = configJson.projects['test-project'].architect; - appArchitect.test.configurations = { - production: { - fileReplacements: [ - { - replace: 'src/environment.ts', - with: 'src/environment.prod.ts', - }, - ], - }, - }; - }), - ) - - // Tests can run in different environment. - .then(() => - writeFile( - 'src/app/environment.spec.ts', - ` + }); + + await ng('test', '--watch=false'); + + await updateJsonFile('angular.json', (configJson) => { + const appArchitect = configJson.projects['test-project'].architect; + appArchitect[isWebpack ? 'test' : 'build'].configurations = { + production: { + fileReplacements: [ + { + replace: 'src/environment.ts', + with: 'src/environment.prod.ts', + }, + ], + }, + }; + if (!isWebpack) { + appArchitect.test.options ??= {}; + appArchitect.test.options.buildTarget = '::production'; + } + }); + + // Tests can run in different environment. + + await writeFile( + 'src/app/environment.spec.ts', + ` import { environment } from '../environment'; describe('Test environment', () => { @@ -55,8 +61,11 @@ export default function () { }); }); `, - ), - ) - .then(() => ng('test', '--configuration=production', '--watch=false')) ); + + if (isWebpack) { + await ng('test', '--watch=false', '--configuration=production'); + } else { + await ng('test', '--watch=false'); + } } diff --git a/tests/legacy-cli/e2e/tests/test/test-scripts.ts b/tests/legacy-cli/e2e/tests/test/test-scripts.ts index acbcc66dc230..1537cdddf349 100644 --- a/tests/legacy-cli/e2e/tests/test/test-scripts.ts +++ b/tests/legacy-cli/e2e/tests/test/test-scripts.ts @@ -1,3 +1,4 @@ +import { getGlobalVariable } from '../../utils/env'; import { writeMultipleFiles } from '../../utils/fs'; import { ng } from '../../utils/process'; import { updateJsonFile } from '../../utils/project'; @@ -60,9 +61,10 @@ export default async function () { // should fail because the global scripts were not added to scripts array await expectToFail(() => ng('test', '--watch=false')); + const isWebpack = !getGlobalVariable('argv')['esbuild']; await updateJsonFile('angular.json', (workspaceJson) => { const appArchitect = workspaceJson.projects['test-project'].architect; - appArchitect.test.options.scripts = [ + appArchitect[isWebpack ? 'test' : 'build'].options.scripts = [ { input: 'src/string-script.js' }, { input: 'src/input-script.js' }, ]; diff --git a/tests/legacy-cli/e2e/tests/test/test-sourcemap.ts b/tests/legacy-cli/e2e/tests/test/test-sourcemap.ts index 9e2a8e3f36fa..6c1cf16cd7b3 100644 --- a/tests/legacy-cli/e2e/tests/test/test-sourcemap.ts +++ b/tests/legacy-cli/e2e/tests/test/test-sourcemap.ts @@ -1,10 +1,13 @@ import assert from 'node:assert'; -import { getGlobalVariable } from '../../utils/env'; import { writeFile } from '../../utils/fs'; import { ng } from '../../utils/process'; import { assertIsError } from '../../utils/utils'; +import { updateJsonFile } from '../../utils/project'; +import { getGlobalVariable } from '../../utils/env'; export default async function () { + const isWebpack = !getGlobalVariable('argv')['esbuild']; + await writeFile( 'src/app/app.spec.ts', ` @@ -15,8 +18,16 @@ export default async function () { ); // when sourcemaps are 'on' the stacktrace will point to the spec.ts file. + await updateJsonFile('angular.json', (configJson) => { + const appArchitect = configJson.projects['test-project'].architect; + if (isWebpack) { + appArchitect['test'].options.sourceMap = true; + } else { + appArchitect['build'].configurations.development.sourceMap = true; + } + }); try { - await ng('test', '--no-watch', '--source-map'); + await ng('test', '--no-watch'); throw new Error('ng test should have failed.'); } catch (error) { assertIsError(error); @@ -25,8 +36,16 @@ export default async function () { } // when sourcemaps are 'off' the stacktrace won't point to the spec.ts file. + await updateJsonFile('angular.json', (configJson) => { + const appArchitect = configJson.projects['test-project'].architect; + if (isWebpack) { + appArchitect['test'].options.sourceMap = false; + } else { + appArchitect['build'].configurations.development.sourceMap = false; + } + }); try { - await ng('test', '--no-watch', '--no-source-map'); + await ng('test', '--no-watch'); throw new Error('ng test should have failed.'); } catch (error) { assertIsError(error); diff --git a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts b/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts index c4c0fd34ec75..90518080f8f3 100644 --- a/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts +++ b/tests/legacy-cli/e2e/tests/vite/ssr-with-ssl.ts @@ -1,3 +1,4 @@ +import { Agent } from 'undici'; import assert from 'node:assert'; import { writeMultipleFiles } from '../../utils/fs'; import { ng, silentNg } from '../../utils/process'; @@ -41,19 +42,30 @@ export default async function () { const port = await ngServe('--ssl'); - // Verify the server is running and the API response is correct. - await validateResponse('/main.js', /bootstrapApplication/); - await validateResponse('/home', /home works/); - - async function validateResponse(pathname: string, match: RegExp): Promise { - try { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; - const response = await fetch(new URL(pathname, `https://localhost:${port}`)); - const text = await response.text(); - assert.match(text, match); - assert.equal(response.status, 200); - } finally { - process.env.NODE_TLS_REJECT_UNAUTHORIZED = '1'; - } + // http 2 + await validateResponse('/main.js', /bootstrapApplication/, true); + await validateResponse('/home', /home works/, true); + + // http 1.1 + await validateResponse('/main.js', /bootstrapApplication/, false); + await validateResponse('/home', /home works/, false); + + async function validateResponse( + pathname: string, + match: RegExp, + allowH2: boolean, + ): Promise { + const response = await fetch(new URL(pathname, `https://localhost:${port}`), { + dispatcher: new Agent({ + connect: { + allowH2, + rejectUnauthorized: false, + }, + }), + }); + + const text = await response.text(); + assert.match(text, match); + assert.equal(response.status, 200); } } diff --git a/tests/legacy-cli/e2e/tests/vitest/basic.ts b/tests/legacy-cli/e2e/tests/vitest/basic.ts deleted file mode 100644 index 5d2f2c3e2b37..000000000000 --- a/tests/legacy-cli/e2e/tests/vitest/basic.ts +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'node:assert/strict'; -import { applyVitestBuilder } from '../../utils/vitest'; -import { ng } from '../../utils/process'; - -export default async function (): Promise { - await applyVitestBuilder(); - - const { stderr } = await ng('test'); - - assert.match( - stderr, - /NOTE: The "unit-test" builder is currently EXPERIMENTAL/, - 'Expected stderr to include the experimental notice.', - ); -} diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts b/tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts new file mode 100644 index 000000000000..b25f8168c5f7 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/browser-no-globals.ts @@ -0,0 +1,31 @@ +import assert from 'node:assert/strict'; +import { writeFile } from '../../utils/fs'; +import { installPackage } from '../../utils/packages'; +import { ng } from '../../utils/process'; +import { applyVitestBuilder } from '../../utils/vitest'; + +/** + * Allow `vitest` import in browser mode. + * @see https://github.com/angular/angular-cli/issues/31745 + */ +export default async function (): Promise { + await applyVitestBuilder(); + + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + + await writeFile( + 'src/app/app.spec.ts', + ` + import { test, expect } from 'vitest'; + + test('should pass', () => { + expect(true).toBe(true); + }); + `, + ); + + const { stdout } = await ng('test', '--browsers', 'ChromiumHeadless'); + + assert.match(stdout, /1 passed/, 'Expected 1 tests to pass.'); +} diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts b/tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts new file mode 100644 index 000000000000..fa9ec43aabf3 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/browser-playwright.ts @@ -0,0 +1,32 @@ +import assert from 'node:assert/strict'; +import { applyVitestBuilder } from '../../utils/vitest'; +import { ng } from '../../utils/process'; +import { installPackage } from '../../utils/packages'; +import { writeFile } from '../../utils/fs'; + +export default async function (): Promise { + await applyVitestBuilder(); + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + await ng('generate', 'component', 'my-comp'); + + await writeFile( + 'src/setup1.ts', + ` + import { getTestBed } from '@angular/core/testing'; + + getTestBed().configureTestingModule({}); + `, + ); + + const { stdout } = await ng( + 'test', + '--no-watch', + '--browsers', + 'chromiumHeadless', + '--setup-files', + 'src/setup1.ts', + ); + + assert.match(stdout, /2 passed/, 'Expected 2 tests to pass.'); +} diff --git a/tests/legacy-cli/e2e/tests/vitest/browser-webdriverio.ts b/tests/legacy-cli/e2e/tests/vitest/browser-webdriverio.ts new file mode 100644 index 000000000000..4ea1b913c3b0 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/browser-webdriverio.ts @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import { applyVitestBuilder } from '../../utils/vitest'; +import { ng } from '../../utils/process'; +import { installPackage } from '../../utils/packages'; +import { writeFile } from '../../utils/fs'; + +export default async function (): Promise { + await applyVitestBuilder(); + await installPackage('webdriverio@9'); + await installPackage('@vitest/browser-webdriverio@4'); + + await ng('generate', 'component', 'my-comp'); + + await writeFile( + 'src/setup1.ts', + ` + import { getTestBed } from '@angular/core/testing'; + + getTestBed().configureTestingModule({}); + `, + ); + + const { stdout } = await ng( + 'test', + '--no-watch', + '--browsers', + 'chromeHeadless', + '--setup-files', + 'src/setup1.ts', + ); + + assert.match(stdout, /2 passed/, 'Expected 2 tests to pass.'); +} diff --git a/tests/legacy-cli/e2e/tests/vitest/include.ts b/tests/legacy-cli/e2e/tests/vitest/include.ts new file mode 100644 index 000000000000..4585194ef3c2 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/include.ts @@ -0,0 +1,14 @@ +import assert from 'node:assert/strict'; +import { applyVitestBuilder } from '../../utils/vitest'; +import { ng } from '../../utils/process'; +import path from 'node:path'; + +export default async function (): Promise { + await applyVitestBuilder(); + + const { stdout: stdout1 } = await ng('test', '--include', path.resolve('src/app/app.spec.ts')); + assert.match(stdout1, /1 passed/, 'Expected 1 test to pass with absolute include.'); + + const { stdout: stdout2 } = await ng('test', '--include', path.normalize('src/app/app.spec.ts')); + assert.match(stdout2, /1 passed/, 'Expected 1 test to pass with relative include.'); +} diff --git a/tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts b/tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts new file mode 100644 index 000000000000..3594bdc7dfee --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/larger-project-coverage.ts @@ -0,0 +1,111 @@ +import { ng } from '../../utils/process'; +import { applyVitestBuilder } from '../../utils/vitest'; +import assert from 'node:assert'; +import { installPackage } from '../../utils/packages'; +import { updateJsonFile } from '../../utils/project'; +import { readFile } from '../../utils/fs'; + +export default async function () { + await applyVitestBuilder(); + await installPackage('@vitest/coverage-v8@4'); + + // Add coverage and threshold configuration to ensure coverage is calculated. + // Use the 'json' reporter to get a machine-readable output for assertions. + await updateJsonFile('angular.json', (json) => { + const project = Object.values(json['projects'])[0] as any; + const test = project['architect']['test']; + test.options = { + coverageReporters: ['json', 'text'], + coverageThresholds: { + // The generated component/service/pipe files are basic + // A threshold of 75 should be safe. + statements: 75, + }, + }; + }); + + const artifactCount = 100; + const initialTestCount = 1; + const generatedFiles = await generateArtifactsInBatches(artifactCount); + + const totalTests = initialTestCount + artifactCount; + const expectedMessage = new RegExp(`${totalTests} passed`); + const coverageJsonPath = 'coverage/test-project/coverage-final.json'; + + // Run tests in default (JSDOM) mode with coverage + const { stdout: jsdomStdout } = await ng('test', '--no-watch', '--coverage'); + assert.match(jsdomStdout, expectedMessage, `Expected ${totalTests} tests to pass in JSDOM mode.`); + + // Assert that every generated file is in the coverage report by reading the JSON output. + const jsdomSummary = JSON.parse(await readFile(coverageJsonPath)); + const jsdomSummaryKeys = Object.keys(jsdomSummary); + for (const file of generatedFiles) { + const found = jsdomSummaryKeys.some((key) => key.endsWith(file)); + assert.ok(found, `Expected ${file} to be in the JSDOM coverage report.`); + } + + // Setup for browser mode + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + + // Run tests in browser mode with coverage + const { stdout: browserStdout } = await ng( + 'test', + '--no-watch', + '--coverage', + '--browsers', + 'ChromiumHeadless', + ); + assert.match( + browserStdout, + expectedMessage, + `Expected ${totalTests} tests to pass in browser mode.`, + ); + + // Assert that every generated file is in the coverage report for browser mode. + const browserSummary = JSON.parse(await readFile(coverageJsonPath)); + const browserSummaryKeys = Object.keys(browserSummary); + for (const file of generatedFiles) { + const found = browserSummaryKeys.some((key) => key.endsWith(file)); + assert.ok(found, `Expected ${file} to be in the browser coverage report.`); + } +} + +async function generateArtifactsInBatches(artifactCount: number): Promise { + const BATCH_SIZE = 5; + const generatedFiles: string[] = []; + let commands: Promise[] = []; + + for (let i = 0; i < artifactCount; i++) { + const type = i % 3; + const name = `test-artifact-${i}`; + + let generateType: string; + let fileSuffix: string; + + switch (type) { + case 0: + generateType = 'component'; + fileSuffix = '.ts'; + break; + case 1: + generateType = 'service'; + fileSuffix = '.ts'; + break; + default: + generateType = 'pipe'; + fileSuffix = '-pipe.ts'; + break; + } + + commands.push(ng('generate', generateType, name, '--skip-tests=false')); + generatedFiles.push(`${name}${fileSuffix}`); + + if (commands.length === BATCH_SIZE || i === artifactCount - 1) { + await Promise.all(commands); + commands = []; + } + } + + return generatedFiles; +} diff --git a/tests/legacy-cli/e2e/tests/vitest/larger-project.ts b/tests/legacy-cli/e2e/tests/vitest/larger-project.ts new file mode 100644 index 000000000000..61b18b102c4b --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/larger-project.ts @@ -0,0 +1,70 @@ +import { ng } from '../../utils/process'; +import { applyVitestBuilder } from '../../utils/vitest'; +import assert from 'node:assert'; +import { installPackage } from '../../utils/packages'; +import { exec } from '../../utils/process'; + +export default async function () { + await applyVitestBuilder(); + + const artifactCount = 100; + // A new project starts with 1 test file (app.spec.ts) + // Each generated artifact will add one more test file. + const initialTestCount = 1; + + await generateArtifactsInBatches(artifactCount); + + const totalTests = initialTestCount + artifactCount; + const expectedMessage = new RegExp(`${totalTests} passed`); + + // Run tests in default (JSDOM) mode + const { stdout: jsdomStdout } = await ng('test', '--no-watch'); + assert.match(jsdomStdout, expectedMessage, `Expected ${totalTests} tests to pass in JSDOM mode.`); + + // Setup for browser mode + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + + // Run tests in browser mode + const { stdout: browserStdout } = await ng( + 'test', + '--no-watch', + '--browsers', + 'ChromiumHeadless', + ); + assert.match( + browserStdout, + expectedMessage, + `Expected ${totalTests} tests to pass in browser mode.`, + ); +} + +async function generateArtifactsInBatches(artifactCount: number): Promise { + const BATCH_SIZE = 5; + let commands: Promise[] = []; + + for (let i = 0; i < artifactCount; i++) { + const type = i % 3; + const name = `test-artifact-${i}`; + let generateType: string; + + switch (type) { + case 0: + generateType = 'component'; + break; + case 1: + generateType = 'service'; + break; + default: + generateType = 'pipe'; + break; + } + + commands.push(ng('generate', generateType, name, '--skip-tests=false')); + + if (commands.length === BATCH_SIZE || i === artifactCount - 1) { + await Promise.all(commands); + commands = []; + } + } +} diff --git a/tests/legacy-cli/e2e/tests/vitest/runner-config-path.ts b/tests/legacy-cli/e2e/tests/vitest/runner-config-path.ts new file mode 100644 index 000000000000..456469b2d6a4 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/runner-config-path.ts @@ -0,0 +1,33 @@ +import assert from 'node:assert/strict'; +import path from 'node:path'; +import { writeMultipleFiles } from '../../utils/fs'; +import { ng } from '../../utils/process'; +import { applyVitestBuilder } from '../../utils/vitest'; + +export default async function (): Promise { + await applyVitestBuilder(); + + // Create a custom Vitest configuration file. + const customConfigPath = 'vitest.custom.mjs'; + await writeMultipleFiles({ + [customConfigPath]: ` + import { defineConfig } from 'vitest/config'; + export default defineConfig({ + test: { + // A unique option to confirm this file is being used. + passWithNoTests: true, + }, + }); + `, + }); + + const absoluteConfigPath = path.resolve(customConfigPath); + const { stdout } = await ng('test', `--runner-config=${absoluteConfigPath}`); + + // Assert that the CLI logs the use of the specified configuration file. + assert.match( + stdout, + /vitest\.custom\.mjs/, + 'Expected a message confirming the use of the custom config file.', + ); +} diff --git a/tests/legacy-cli/e2e/tests/vitest/snapshot.ts b/tests/legacy-cli/e2e/tests/vitest/snapshot.ts index d610f8f861dc..f099ba6f8d30 100644 --- a/tests/legacy-cli/e2e/tests/vitest/snapshot.ts +++ b/tests/legacy-cli/e2e/tests/vitest/snapshot.ts @@ -1,5 +1,5 @@ import { ng } from '../../utils/process'; -import { appendToFile, replaceInFile, readFile } from '../../utils/fs'; +import { replaceInFile, readFile, writeFile } from '../../utils/fs'; import { applyVitestBuilder } from '../../utils/vitest'; import assert from 'node:assert/strict'; import { stripVTControlCharacters } from 'node:util'; @@ -9,23 +9,32 @@ export default async function () { await applyVitestBuilder(); // Add snapshot assertions to the test file - await appendToFile( + await replaceInFile( 'src/app/app.spec.ts', + `describe('App', () => {`, ` - it('should match file snapshot', () => { - const fixture = TestBed.createComponent(App); - const app = fixture.componentInstance; - expect((app as any).title()).toMatchSnapshot(); - }); +describe('App', () => { + it('should match file snapshot', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect((app as any).title()).toMatchSnapshot(); + }); - it('should match inline snapshot', () => { - const fixture = TestBed.createComponent(App); - const app = fixture.componentInstance; - expect((app as any).title()).toMatchInlineSnapshot(); - }); - `, + it('should match inline snapshot', () => { + const fixture = TestBed.createComponent(App); + const app = fixture.componentInstance; + expect((app as any).title()).toMatchInlineSnapshot(); + }); +`, ); + // Synchronize line endings for Vitest which currently may miscalculate line counts + // with mixed file line endings. + let content = await readFile('src/app/app.spec.ts'); + content = content.replace(/\r\n/g, '\n'); + content = content.replace(/\r/g, '\n'); + await writeFile('src/app/app.spec.ts', content); + // First run: create snapshots const { stdout: firstRunStdout } = await ng('test'); assert.match( @@ -44,7 +53,7 @@ export default async function () { const snapshotContent = await readFile('src/app/__snapshots__/app.spec.ts.snap'); assert.match( snapshotContent, - /exports\[`should match file snapshot 1`\] = `"test-project"`;/, + /exports\[`App > should match file snapshot 1`\] = `"test-project"`;/, 'File snapshot was not written to disk.', ); diff --git a/tests/legacy-cli/e2e/tests/vitest/tslib-resolution.ts b/tests/legacy-cli/e2e/tests/vitest/tslib-resolution.ts new file mode 100644 index 000000000000..759d5e2b5728 --- /dev/null +++ b/tests/legacy-cli/e2e/tests/vitest/tslib-resolution.ts @@ -0,0 +1,71 @@ +import { writeFile } from '../../utils/fs'; +import { installPackage } from '../../utils/packages'; +import { ng } from '../../utils/process'; +import { applyVitestBuilder } from '../../utils/vitest'; +import assert from 'node:assert'; + +export default async function () { + await applyVitestBuilder(); + await installPackage('playwright@1'); + await installPackage('@vitest/browser-playwright@4'); + + // Add a custom decorator to trigger tslib usage + await writeFile( + 'src/app/custom-decorator.ts', + ` + export function MyDecorator() { + return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { + // do nothing + }; + } + `, + ); + + // Add a service that uses the decorator + await writeFile( + 'src/app/test.service.ts', + ` + import { Injectable } from '@angular/core'; + import { MyDecorator } from './custom-decorator'; + + @Injectable({ + providedIn: 'root' + }) + export class TestService { + @MyDecorator() + myMethod() { + return true; + } + } + `, + ); + + // Add a test for the service + await writeFile( + 'src/app/test.service.spec.ts', + ` + import { TestBed } from '@angular/core/testing'; + import { TestService } from './test.service'; + + describe('TestService', () => { + let service: TestService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(TestService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + it('myMethod should return true', () => { + expect(service.myMethod()).toBe(true); + }); + }); + `, + ); + + const { stdout } = await ng('test', '--no-watch', '--browsers', 'chromiumHeadless'); + assert.match(stdout, /2 passed/, 'Expected 2 tests to pass.'); +} diff --git a/tests/legacy-cli/e2e/utils/assets.ts b/tests/legacy-cli/e2e/utils/assets.ts index d421086c1b9e..153fdaccc6de 100644 --- a/tests/legacy-cli/e2e/utils/assets.ts +++ b/tests/legacy-cli/e2e/utils/assets.ts @@ -5,7 +5,7 @@ import { getGlobalVariable } from './env'; import { resolve } from 'node:path'; import { copyFile } from './fs'; import { installWorkspacePackages, setRegistry } from './packages'; -import { useBuiltPackagesVersions } from './project'; +import { getTestProjectDir, useBuiltPackagesVersions } from './project'; export function assetDir(assetName: string) { return join(__dirname, '../e2e/assets', assetName); @@ -21,7 +21,7 @@ export function copyProjectAsset(assetName: string, to?: string) { export function copyAssets(assetName: string, to?: string) { const seed = +Date.now(); - const tempRoot = join(getGlobalVariable('projects-root'), 'assets', assetName + '-' + seed); + const tempRoot = join(getTestAssetsDir(), assetName + '-' + seed); const root = assetDir(assetName); return Promise.resolve() @@ -30,9 +30,7 @@ export function copyAssets(assetName: string, to?: string) { return allFiles.reduce((promise, filePath) => { const toPath = - to !== undefined - ? resolve(getGlobalVariable('projects-root'), 'test-project', to, filePath) - : join(tempRoot, filePath); + to !== undefined ? resolve(getTestProjectDir(), to, filePath) : join(tempRoot, filePath); return promise .then(() => copyFile(join(root, filePath), toPath)) @@ -65,3 +63,7 @@ export async function createProjectFromAsset( return () => setRegistry(true /** useTestRegistry */); } + +export function getTestAssetsDir(): string { + return join(getGlobalVariable('projects-root'), 'assets'); +} diff --git a/tests/legacy-cli/e2e/utils/project.ts b/tests/legacy-cli/e2e/utils/project.ts index ea764bb20314..fe9cfd8a0e04 100644 --- a/tests/legacy-cli/e2e/utils/project.ts +++ b/tests/legacy-cli/e2e/utils/project.ts @@ -7,6 +7,7 @@ import { gitCommit } from './git'; import { findFreePort } from './network'; import { installWorkspacePackages, PkgInfo } from './packages'; import { execAndWaitForOutputToMatch, git, ng } from './process'; +import { join } from 'node:path'; export function updateJsonFile(filePath: string, fn: (json: any) => any | void) { return readFile(filePath).then((tsConfigJson) => { @@ -191,7 +192,11 @@ export async function useCIChrome(projectName: string, projectDir = ''): Promise return updateJsonFile('angular.json', (workspaceJson) => { const project = workspaceJson.projects[projectName]; const appTargets = project.targets || project.architect; - appTargets.test.options.browsers = 'ChromeHeadlessNoSandbox'; + if (appTargets.test.builder === '@angular/build:unit-test') { + appTargets.test.options.browsers = ['ChromeHeadlessNoSandbox']; + } else { + appTargets.test.options.browsers = 'ChromeHeadlessNoSandbox'; + } }); } @@ -266,3 +271,7 @@ export function updateServerFileForEsbuild(filepath: string): Promise { `, ); } + +export function getTestProjectDir(): string { + return join(getGlobalVariable('projects-root'), 'test-project'); +} diff --git a/tests/legacy-cli/e2e/utils/registry.ts b/tests/legacy-cli/e2e/utils/registry.ts index 1bd3084d4f48..b4c1b08afcbc 100644 --- a/tests/legacy-cli/e2e/utils/registry.ts +++ b/tests/legacy-cli/e2e/utils/registry.ts @@ -1,9 +1,10 @@ import { ChildProcess, fork } from 'node:child_process'; import { on } from 'node:events'; +import { mkdir } from 'node:fs/promises'; import { join } from 'node:path'; import { getGlobalVariable } from './env'; import { writeFile, readFile } from './fs'; -import { mktempd } from './utils'; +import { existsSync } from 'node:fs'; export async function createNpmRegistry( port: number, @@ -11,14 +12,18 @@ export async function createNpmRegistry( withAuthentication = false, ): Promise { // Setup local package registry - const registryPath = await mktempd('angular-cli-e2e-registry-'); + const registryPath = join(getGlobalVariable('tmp-root'), 'registry'); + if (!existsSync(registryPath)) { + await mkdir(registryPath); + } + + const configFileName = withAuthentication ? 'verdaccio_auth.yaml' : 'verdaccio.yaml'; + let configContent = await readFile(join(__dirname, '../', configFileName)); + configContent = configContent + .replace(/\$\{HTTP_PORT\}/g, String(port)) + .replace(/\$\{HTTPS_PORT\}/g, String(httpsPort)); + const configPath = join(registryPath, configFileName); - let configContent = await readFile( - join(__dirname, '../', withAuthentication ? 'verdaccio_auth.yaml' : 'verdaccio.yaml'), - ); - configContent = configContent.replace(/\$\{HTTP_PORT\}/g, String(port)); - configContent = configContent.replace(/\$\{HTTPS_PORT\}/g, String(httpsPort)); - const configPath = join(registryPath, 'verdaccio.yaml'); await writeFile(configPath, configContent); const verdaccioServer = fork(require.resolve('verdaccio/bin/verdaccio'), ['-c', configPath]); diff --git a/tests/legacy-cli/e2e/utils/vitest.ts b/tests/legacy-cli/e2e/utils/vitest.ts index a16d7fca3cee..af40e66b18b1 100644 --- a/tests/legacy-cli/e2e/utils/vitest.ts +++ b/tests/legacy-cli/e2e/utils/vitest.ts @@ -3,7 +3,8 @@ import { updateJsonFile } from './project'; /** Updates the `test` builder in the current workspace to use Vitest. */ export async function applyVitestBuilder(): Promise { - await silentNpm('install', 'vitest@4.0.0', 'jsdom@27.0.0', '--save-dev'); + // These deps matches the deps in `@schematics/angular` + await silentNpm('install', 'vitest@^4.0.8', 'jsdom@^27.1.0', '--save-dev'); await updateJsonFile('angular.json', (json) => { const projects = Object.values(json['projects']); diff --git a/tests/legacy-cli/e2e_runner.ts b/tests/legacy-cli/e2e_runner.ts index 5d7031f20489..6c9e3179411c 100644 --- a/tests/legacy-cli/e2e_runner.ts +++ b/tests/legacy-cli/e2e_runner.ts @@ -4,6 +4,7 @@ import colors from 'ansi-colors'; import glob from 'fast-glob'; import * as path from 'node:path'; import * as fs from 'node:fs'; +import { rm } from 'node:fs/promises'; import { getGlobalVariable, setGlobalVariable } from './e2e/utils/env'; import { gitClean } from './e2e/utils/git'; import { createNpmRegistry } from './e2e/utils/registry'; @@ -13,7 +14,8 @@ import { findFreePort } from './e2e/utils/network'; import { extractFile } from './e2e/utils/tar'; import { realpathSync } from 'node:fs'; import { PkgInfo } from './e2e/utils/packages'; -import { rm } from 'node:fs/promises'; +import { getTestProjectDir } from './e2e/utils/project'; +import { mktempd } from './e2e/utils/utils'; Error.stackTraceLimit = Infinity; @@ -30,13 +32,9 @@ Error.stackTraceLimit = Infinity; * --ng-snapshots Install angular snapshot builds in the test project. * --glob Run tests matching this glob pattern (relative to tests/e2e/). * --ignore Ignore tests matching this glob pattern. - * --reuse=/path Use a path instead of create a new project. That project should have been - * created, and npm installed. Ideally you want a project created by a previous - * run of e2e. * --nb-shards Total number of shards that this is part of. Default is 2 if --shard is * passed in. * --shard Index of this processes' shard. - * --tmpdir=path Override temporary directory to use for new projects. * --package-manager Package manager to use. * --package=path An npm package to be published before running tests * @@ -57,8 +55,6 @@ const parsed = parseArgs({ 'nosilent': { type: 'boolean' }, 'package': { type: 'string', multiple: true, default: ['./dist/_*.tgz'] }, 'package-manager': { type: 'string', default: 'npm' }, - 'reuse': { type: 'string' }, - 'tmpdir': { type: 'string' }, 'verbose': { type: 'boolean' }, 'nb-shards': { type: 'string' }, @@ -125,7 +121,7 @@ const logger = createConsoleLogger(argv.verbose, process.stdout, process.stderr, const logStack = [logger]; function lastLogger() { - return logStack[logStack.length - 1]; + return logStack.at(-1)!; } // Under bazel the compiled file (.js) and types (.d.ts) are available. @@ -226,57 +222,68 @@ process.env.CHROME_BIN = path.resolve(process.env.CHROME_BIN!); process.env.CHROME_PATH = path.resolve(process.env.CHROME_PATH!); process.env.CHROMEDRIVER_BIN = path.resolve(process.env.CHROMEDRIVER_BIN!); -Promise.all([findFreePort(), findFreePort(), findPackageTars()]) - .then(async ([httpPort, httpsPort, packageTars]) => { - setGlobalVariable('package-registry', 'http://localhost:' + httpPort); - setGlobalVariable('package-secure-registry', 'http://localhost:' + httpsPort); - setGlobalVariable('package-tars', packageTars); - - // NPM registries for the lifetime of the test execution - const registryProcess = await createNpmRegistry(httpPort, httpPort); - const secureRegistryProcess = await createNpmRegistry(httpPort, httpsPort, true); - - try { - await runSteps(runSetup, allSetups, 'setup'); - await runSteps(runInitializer, allInitializers, 'initializer'); - await runSteps(runTest, testsToRun, 'test'); - - if (shardId !== null) { - console.log(colors.green(`Done shard ${shardId} of ${nbShards}.`)); - } else { - console.log(colors.green('Done.')); - } +(async () => { + const tempRoot = await mktempd('angular-cli-e2e-', process.env.E2E_TEMP); + setGlobalVariable('tmp-root', tempRoot); + + process.on('SIGINT', deleteTemporaryRoot); + process.on('exit', deleteTemporaryRoot); + + const [httpPort, httpsPort, packageTars] = await Promise.all([ + findFreePort(), + findFreePort(), + findPackageTars(), + ]); + setGlobalVariable('package-registry', 'http://localhost:' + httpPort); + setGlobalVariable('package-secure-registry', 'http://localhost:' + httpsPort); + setGlobalVariable('package-tars', packageTars); + + // NPM registries for the lifetime of the test execution + const registryProcess = await createNpmRegistry(httpPort, httpPort); + const secureRegistryProcess = await createNpmRegistry(httpPort, httpsPort, true); + + try { + console.log(` Using "${tempRoot}" as temporary directory for a new project.`); + + await runSteps(runSetup, allSetups, 'setup'); + await runSteps(runInitializer, allInitializers, 'initializer'); + await runSteps(runTest, testsToRun, 'test'); + + if (shardId !== null) { + console.log(colors.green(`Done shard ${shardId} of ${nbShards}.`)); + } else { + console.log(colors.green('Done.')); + } - process.exitCode = 0; - } catch (err) { - if (err instanceof Error) { - console.log('\n'); - console.error(colors.red(err.message)); - if (err.stack) { - console.error(colors.red(err.stack)); - } - } else { - console.error(colors.red(String(err))); + process.exitCode = 0; + } catch (err) { + if (err instanceof Error) { + console.log('\n'); + console.error(colors.red(err.message)); + if (err.stack) { + console.error(colors.red(err.stack)); } + } else { + console.error(colors.red(String(err))); + } - if (argv.debug) { - console.log(`Current Directory: ${process.cwd()}`); - console.log('Will loop forever while you debug... CTRL-C to quit.'); + if (argv.debug) { + console.log(`Current Directory: ${process.cwd()}`); + console.log('Will loop forever while you debug... CTRL-C to quit.'); - // Wait forever until user explicitly cancels. - await new Promise(() => {}); - } - - process.exitCode = 1; - } finally { - registryProcess.kill(); - secureRegistryProcess.kill(); + // Wait forever until user explicitly cancels. + await new Promise(() => {}); } - }) - .catch((err) => { - console.error(colors.red(`Unkown Error: ${err}`)); + process.exitCode = 1; - }); + } finally { + registryProcess.kill(); + secureRegistryProcess.kill(); + } +})().catch((err) => { + console.error(colors.red(`Unkown Error: ${err}`)); + process.exitCode = 1; +}); async function runSteps( run: (name: string) => Promise | void, @@ -334,7 +341,7 @@ function runInitializer(absoluteName: string): Promise { * Run a file from the main 'test-project' directory in a subprocess via launchTestProcess(). */ async function runTest(absoluteName: string): Promise { - process.chdir(join(getGlobalVariable('projects-root'), 'test-project')); + process.chdir(getTestProjectDir()); await launchTestProcess(absoluteName); await cleanTestProject(); @@ -343,7 +350,7 @@ async function runTest(absoluteName: string): Promise { async function cleanTestProject() { await gitClean(); - const testProject = join(getGlobalVariable('projects-root'), 'test-project'); + const testProject = getTestProjectDir(); // Note: Dist directory is not cleared between tests, as `git clean` // doesn't delete it. @@ -406,3 +413,13 @@ async function findPackageTars(): Promise<{ [pkg: string]: PkgInfo }> { {} as { [pkg: string]: PkgInfo }, ); } + +function deleteTemporaryRoot(): void { + try { + fs.rmSync(getGlobalVariable('tmp-root'), { + recursive: true, + force: true, + maxRetries: 3, + }); + } catch {} +} diff --git a/tests/legacy-cli/rollup.config.mjs b/tests/legacy-cli/rollup.config.mjs index 0fc2768c5057..208e4cc78c42 100644 --- a/tests/legacy-cli/rollup.config.mjs +++ b/tests/legacy-cli/rollup.config.mjs @@ -25,7 +25,7 @@ for (const file of testFiles) { export default { input: chunks, - external: [], + external: ['undici'], // This cannot be bundled as `node:sqlite` is experimental in node.js 22. Remove once this feature is no longer behind a flag plugins: [ nodeResolve({ preferBuiltins: true, diff --git a/tools/example_db_generator.js b/tools/example_db_generator.js index 142bd1e8a7ed..052bb1afb53d 100644 --- a/tools/example_db_generator.js +++ b/tools/example_db_generator.js @@ -89,6 +89,20 @@ function generate(inPath, outPath) { } const db = new DatabaseSync(dbPath); + // Create a table to store metadata. + db.exec(` + CREATE TABLE metadata ( + key TEXT PRIMARY KEY NOT NULL, + value TEXT NOT NULL + ); + `); + + db.exec(` + INSERT INTO metadata (key, value) VALUES + ('schema_version', '1'), + ('created_at', '${new Date().toISOString()}'); + `); + // Create a relational table to store the structured example data. db.exec(` CREATE TABLE examples ( diff --git a/tools/ng_cli_schema_generator.bzl b/tools/ng_cli_schema_generator.bzl index 9bcc4d287a6a..86d9552dd70c 100644 --- a/tools/ng_cli_schema_generator.bzl +++ b/tools/ng_cli_schema_generator.bzl @@ -4,6 +4,7 @@ def cli_json_schema(name, src, out, data = []): js_run_binary( name = name, outs = [out], + tags = ["schema"], srcs = [src] + data, tool = "//tools:ng_cli_schema", progress_message = "Generating CLI interface from %s" % src, diff --git a/tools/ts_json_schema.bzl b/tools/ts_json_schema.bzl index deb34c0af597..dab651d0d7e0 100644 --- a/tools/ts_json_schema.bzl +++ b/tools/ts_json_schema.bzl @@ -12,6 +12,7 @@ def ts_json_schema(name, src, data = []): name = name + ".interface", outs = [out], srcs = [src] + data, + tags = ["schema"], tool = "//tools:quicktype_runner", progress_message = "Generating TS interface for %s" % src, mnemonic = "TsJsonSchema", diff --git a/tsconfig-build-esm.json b/tsconfig-build-esm.json index 5a548be0a88f..8682ad1fbdc5 100644 --- a/tsconfig-build-esm.json +++ b/tsconfig-build-esm.json @@ -8,7 +8,7 @@ "compilerOptions": { "module": "esnext", "target": "es2022", - "lib": ["es2020"], + "lib": ["es2022"], // don't auto-discover @types/node, it results in a ///